diff --git a/changelogs/fragments/207-acme-account-key-passphrase.yml b/changelogs/fragments/207-acme-account-key-passphrase.yml new file mode 100644 index 00000000..e3675f33 --- /dev/null +++ b/changelogs/fragments/207-acme-account-key-passphrase.yml @@ -0,0 +1,4 @@ +minor_changes: +- "acme_* modules - support account key passphrases for ``cryptography`` backend (https://github.com/ansible-collections/community.crypto/issues/197, https://github.com/ansible-collections/community.crypto/pull/207)." +- "acme_certificate_revoke - support revoking by private keys that are passphrase protected for ``cryptography`` backend (https://github.com/ansible-collections/community.crypto/pull/207)." +- "acme_challenge_cert_helper - add ``private_key_passphrase`` parameter (https://github.com/ansible-collections/community.crypto/pull/207)." diff --git a/plugins/doc_fragments/acme.py b/plugins/doc_fragments/acme.py index 4eccd656..7fbeb74f 100644 --- a/plugins/doc_fragments/acme.py +++ b/plugins/doc_fragments/acme.py @@ -57,6 +57,12 @@ options: Ansible in the process of moving the module with its argument to the node where it is executed." type: str + account_key_passphrase: + description: + - Phassphrase to use to decode the account key. + - "B(Note:) this is not supported by the C(openssl) backend, only by the C(cryptography) backend." + type: str + version_added: 1.6.0 account_uri: description: - "If specified, assumes that the account URI is as given. If the diff --git a/plugins/module_utils/acme/acme.py b/plugins/module_utils/acme/acme.py index 27a8471a..11704209 100644 --- a/plugins/module_utils/acme/acme.py +++ b/plugins/module_utils/acme/acme.py @@ -112,6 +112,7 @@ class ACMEClient(object): # account_key path and content are mutually exclusive self.account_key_file = module.params['account_key_src'] self.account_key_content = module.params['account_key_content'] + self.account_key_passphrase = module.params['account_key_passphrase'] # Grab account URI from module parameters. # Make sure empty string is treated as None. @@ -122,7 +123,10 @@ class ACMEClient(object): self.account_jws_header = None if self.account_key_file is not None or self.account_key_content is not None: try: - self.account_key_data = self.parse_key(key_file=self.account_key_file, key_content=self.account_key_content) + self.account_key_data = self.parse_key( + key_file=self.account_key_file, + key_content=self.account_key_content, + passphrase=self.account_key_passphrase) except KeyParsingError as e: raise ModuleFailException("Error while parsing account key: {msg}".format(msg=e.msg)) self.account_jwk = self.account_key_data['jwk'] @@ -146,14 +150,14 @@ class ACMEClient(object): self.account_jws_header.pop('jwk') self.account_jws_header['kid'] = self.account_uri - def parse_key(self, key_file=None, key_content=None): + def parse_key(self, key_file=None, key_content=None, passphrase=None): ''' Parses an RSA or Elliptic Curve key file in PEM format and returns key_data. In case of an error, raises KeyParsingError. ''' if key_file is None and key_content is None: raise AssertionError('One of key_file and key_content must be specified!') - error, key_data = self.backend.parse_key(key_file, key_content) + error, key_data = self.backend.parse_key(key_file, key_content, passphrase=passphrase) if error: raise KeyParsingError(error) return key_data @@ -311,6 +315,7 @@ def get_default_argspec(): return dict( account_key_src=dict(type='path', aliases=['account_key']), account_key_content=dict(type='str', no_log=True), + account_key_passphrase=dict(type='str', no_log=True), account_uri=dict(type='str'), acme_directory=dict(type='str'), acme_version=dict(type='int', choices=[1, 2]), diff --git a/plugins/module_utils/acme/backend_cryptography.py b/plugins/module_utils/acme/backend_cryptography.py index 77366ca2..66d5918f 100644 --- a/plugins/module_utils/acme/backend_cryptography.py +++ b/plugins/module_utils/acme/backend_cryptography.py @@ -179,7 +179,7 @@ class CryptographyBackend(CryptoBackend): def __init__(self, module): super(CryptographyBackend, self).__init__(module) - def parse_key(self, key_file=None, key_content=None): + def parse_key(self, key_file=None, key_content=None, passphrase=None): ''' Parses an RSA or Elliptic Curve key file in PEM format and returns a pair (error, key_data). @@ -191,7 +191,10 @@ class CryptographyBackend(CryptoBackend): key_content = to_bytes(key_content) # Parse key try: - key = cryptography.hazmat.primitives.serialization.load_pem_private_key(key_content, password=None, backend=_cryptography_backend) + key = cryptography.hazmat.primitives.serialization.load_pem_private_key( + key_content, + password=to_bytes(passphrase) if passphrase is not None else None, + backend=_cryptography_backend) except Exception as e: return 'error while loading key: {0}'.format(e), None if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey): diff --git a/plugins/module_utils/acme/backend_openssl_cli.py b/plugins/module_utils/acme/backend_openssl_cli.py index fdbe6cb5..47d8cfbd 100644 --- a/plugins/module_utils/acme/backend_openssl_cli.py +++ b/plugins/module_utils/acme/backend_openssl_cli.py @@ -41,11 +41,13 @@ class OpenSSLCLIBackend(CryptoBackend): openssl_binary = module.get_bin_path('openssl', True) self.openssl_binary = openssl_binary - def parse_key(self, key_file=None, key_content=None): + def parse_key(self, key_file=None, key_content=None, passphrase=None): ''' Parses an RSA or Elliptic Curve key file in PEM format and returns a pair (error, key_data). ''' + if passphrase is not None: + return 'openssl backend does not support key passphrases', {} # If key_file isn't given, but key_content, write that to a temporary file if key_file is None: fd, tmpsrc = tempfile.mkstemp() diff --git a/plugins/module_utils/acme/backends.py b/plugins/module_utils/acme/backends.py index afd364c3..a61ff53b 100644 --- a/plugins/module_utils/acme/backends.py +++ b/plugins/module_utils/acme/backends.py @@ -19,7 +19,7 @@ class CryptoBackend(object): self.module = module @abc.abstractmethod - def parse_key(self, key_file=None, key_content=None): + def parse_key(self, key_file=None, key_content=None, passphrase=None): ''' Parses an RSA or Elliptic Curve key file in PEM format and returns a pair (error, key_data). diff --git a/plugins/modules/acme_account.py b/plugins/modules/acme_account.py index 814f690a..bb90e0b0 100644 --- a/plugins/modules/acme_account.py +++ b/plugins/modules/acme_account.py @@ -88,6 +88,12 @@ options: - "Mutually exclusive with C(new_account_key_src)." - "Required if C(new_account_key_src) is not used and state is C(changed_key)." type: str + new_account_key_passphrase: + description: + - Phassphrase to use to decode the new account key. + - "B(Note:) this is not supported by the C(openssl) backend, only by the C(cryptography) backend." + type: str + version_added: 1.6.0 external_account_binding: description: - Allows to provide external account binding data during account creation. @@ -183,6 +189,7 @@ def main(): contact=dict(type='list', elements='str', default=[]), new_account_key_src=dict(type='path'), new_account_key_content=dict(type='str', no_log=True), + new_account_key_passphrase=dict(type='str', no_log=True), external_account_binding=dict(type='dict', options=dict( kid=dict(type='str', required=True), alg=dict(type='str', required=True, choices=['HS256', 'HS384', 'HS512']), @@ -272,7 +279,8 @@ def main(): try: new_key_data = client.parse_key( module.params.get('new_account_key_src'), - module.params.get('new_account_key_content') + module.params.get('new_account_key_content'), + passphrase=module.params.get('new_account_key_passphrase'), ) except KeyParsingError as e: raise ModuleFailException("Error while parsing new account key: {msg}".format(msg=e.msg)) diff --git a/plugins/modules/acme_certificate_revoke.py b/plugins/modules/acme_certificate_revoke.py index fa33f81b..b25bdfba 100644 --- a/plugins/modules/acme_certificate_revoke.py +++ b/plugins/modules/acme_certificate_revoke.py @@ -54,7 +54,6 @@ options: private keys in PEM format can be used as well." - "Mutually exclusive with C(account_key_content)." - "Required if C(account_key_content) is not used." - type: path account_key_content: description: - "Content of the ACME account RSA or Elliptic Curve key." @@ -69,7 +68,6 @@ options: temporary file. It can still happen that it is written to disk by Ansible in the process of moving the module with its argument to the node where it is executed." - type: str private_key_src: description: - "Path to the certificate's private key." @@ -91,6 +89,12 @@ options: Ansible in the process of moving the module with its argument to the node where it is executed." type: str + private_key_passphrase: + description: + - Phassphrase to use to decode the certificate's private key. + - "B(Note:) this is not supported by the C(openssl) backend, only by the C(cryptography) backend." + type: str + version_added: 1.6.0 revoke_reason: description: - "One of the revocation reasonCodes defined in @@ -146,6 +150,7 @@ def main(): argument_spec.update(dict( private_key_src=dict(type='path'), private_key_content=dict(type='str', no_log=True), + private_key_passphrase=dict(type='str', no_log=True), certificate=dict(type='path', required=True), revoke_reason=dict(type='int'), )) @@ -184,9 +189,10 @@ def main(): private_key_content = module.params.get('private_key_content') # Revoke certificate if private_key or private_key_content: + passphrase = module.params['private_key_passphrase'] # Step 1: load and parse private key try: - private_key_data = client.parse_key(private_key, private_key_content) + private_key_data = client.parse_key(private_key, private_key_content, passphrase=passphrase) except KeyParsingError as e: raise ModuleFailException("Error while parsing private key: {msg}".format(msg=e.msg)) # Step 2: sign revokation request with private key diff --git a/plugins/modules/acme_challenge_cert_helper.py b/plugins/modules/acme_challenge_cert_helper.py index d18c3dec..6dbe3fc3 100644 --- a/plugins/modules/acme_challenge_cert_helper.py +++ b/plugins/modules/acme_challenge_cert_helper.py @@ -52,6 +52,11 @@ options: - "Content of the private key to use for this challenge certificate." - "Mutually exclusive with C(private_key_src)." type: str + private_key_passphrase: + description: + - Phassphrase to use to decode the private key. + type: str + version_added: 1.6.0 notes: - Does not support C(check_mode). ''' @@ -187,6 +192,7 @@ def main(): challenge_data=dict(type='dict', required=True), private_key_src=dict(type='path'), private_key_content=dict(type='str', no_log=True), + private_key_passphrase=dict(type='str', no_log=True), ), required_one_of=( ['private_key_src', 'private_key_content'], @@ -205,12 +211,16 @@ def main(): # Get hold of private key private_key_content = module.params.get('private_key_content') + private_key_passphrase = module.params.get('private_key_passphrase') if private_key_content is None: private_key_content = read_file(module.params['private_key_src']) else: private_key_content = to_bytes(private_key_content) try: - private_key = cryptography.hazmat.primitives.serialization.load_pem_private_key(private_key_content, password=None, backend=_cryptography_backend) + private_key = cryptography.hazmat.primitives.serialization.load_pem_private_key( + private_key_content, + password=to_bytes(private_key_passphrase) if private_key_passphrase is not None else None, + backend=_cryptography_backend) except Exception as e: raise ModuleFailException('Error while loading private key: {0}'.format(e)) diff --git a/tests/integration/targets/acme_account/tasks/impl.yml b/tests/integration/targets/acme_account/tasks/impl.yml index 4a0ff616..a50d53fd 100644 --- a/tests/integration/targets/acme_account/tasks/impl.yml +++ b/tests/integration/targets/acme_account/tasks/impl.yml @@ -1,20 +1,29 @@ -- name: Generate account keys - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/{{ item }}.pem" - loop: - - accountkey - - accountkey2 - - accountkey3 - - accountkey4 - - accountkey5 +- block: + - name: Generate account keys + openssl_privatekey: + path: "{{ output_dir }}/{{ item.name }}.pem" + passphrase: "{{ item.pass | default(omit, true) }}" + cipher: "{{ 'auto' if item.pass | default() else omit }}" + type: ECC + curve: secp256r1 + force: true + loop: "{{ account_keys }}" -- name: Parse account keys (to ease debugging some test failures) - command: "{{ openssl_binary }} ec -in {{ output_dir }}/{{ item }}.pem -noout -text" - loop: - - accountkey - - accountkey2 - - accountkey3 - - accountkey4 - - accountkey5 + - name: Parse account keys (to ease debugging some test failures) + openssl_privatekey_info: + path: "{{ output_dir }}/{{ item.name }}.pem" + passphrase: "{{ item.pass | default(omit, true) }}" + return_private_key_data: true + loop: "{{ account_keys }}" + + vars: + account_keys: + - name: accountkey + - name: accountkey2 + pass: "{{ 'hunter2' if select_crypto_backend != 'openssl' else '' }}" + - name: accountkey3 + - name: accountkey4 + - name: accountkey5 - name: Do not try to create account acme_account: @@ -173,6 +182,7 @@ acme_directory: https://{{ acme_host }}:14000/dir validate_certs: no new_account_key_src: "{{ output_dir }}/accountkey2.pem" + new_account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" state: changed_key contact: - mailto:example@example.com @@ -188,6 +198,7 @@ acme_directory: https://{{ acme_host }}:14000/dir validate_certs: no new_account_key_src: "{{ output_dir }}/accountkey2.pem" + new_account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" state: changed_key contact: - mailto:example@example.com @@ -197,6 +208,7 @@ acme_account: select_crypto_backend: "{{ select_crypto_backend }}" account_key_src: "{{ output_dir }}/accountkey2.pem" + account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" acme_version: 2 acme_directory: https://{{ acme_host }}:14000/dir validate_certs: no @@ -209,6 +221,7 @@ acme_account: select_crypto_backend: "{{ select_crypto_backend }}" account_key_src: "{{ output_dir }}/accountkey2.pem" + account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" acme_version: 2 acme_directory: https://{{ acme_host }}:14000/dir validate_certs: no @@ -219,6 +232,7 @@ acme_account: select_crypto_backend: "{{ select_crypto_backend }}" account_key_src: "{{ output_dir }}/accountkey2.pem" + account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" acme_version: 2 acme_directory: https://{{ acme_host }}:14000/dir validate_certs: no @@ -229,6 +243,7 @@ acme_account: select_crypto_backend: "{{ select_crypto_backend }}" account_key_src: "{{ output_dir }}/accountkey2.pem" + account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" acme_version: 2 acme_directory: https://{{ acme_host }}:14000/dir validate_certs: no diff --git a/tests/integration/targets/acme_account_info/tasks/impl.yml b/tests/integration/targets/acme_account_info/tasks/impl.yml index 552fc0b2..1caec95d 100644 --- a/tests/integration/targets/acme_account_info/tasks/impl.yml +++ b/tests/integration/targets/acme_account_info/tasks/impl.yml @@ -1,12 +1,23 @@ --- -- name: Generate account key - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem" +- block: + - name: Generate account keys + openssl_privatekey: + path: "{{ output_dir }}/{{ item }}.pem" + type: ECC + curve: secp256r1 + force: true + loop: "{{ account_keys }}" -- name: Generate second account key - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey2.pem" + - name: Parse account keys (to ease debugging some test failures) + openssl_privatekey_info: + path: "{{ output_dir }}/{{ item }}.pem" + return_private_key_data: true + loop: "{{ account_keys }}" -- name: Parse account key (to ease debugging some test failures) - command: "{{ openssl_binary }} ec -in {{ output_dir }}/accountkey.pem -noout -text" + vars: + account_keys: + - accountkey + - accountkey2 - name: Check that account does not exist acme_account_info: diff --git a/tests/integration/targets/acme_certificate/tasks/impl.yml b/tests/integration/targets/acme_certificate/tasks/impl.yml index 8547245d..7faedb0e 100644 --- a/tests/integration/targets/acme_certificate/tasks/impl.yml +++ b/tests/integration/targets/acme_certificate/tasks/impl.yml @@ -1,11 +1,26 @@ --- ## SET UP ACCOUNT KEYS ######################################################################## -- name: Create ECC256 account key - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem" -- name: Create ECC384 account key - command: "{{ openssl_binary }} ecparam -name secp384r1 -genkey -out {{ output_dir }}/account-ec384.pem" -- name: Create RSA account key - command: "{{ openssl_binary }} genrsa -out {{ output_dir }}/account-rsa.pem {{ default_rsa_key_size }}" +- block: + - name: Generate account keys + openssl_privatekey: + path: "{{ output_dir }}/{{ item.name }}.pem" + type: "{{ item.type }}" + size: "{{ item.size | default(omit) }}" + curve: "{{ item.curve | default(omit) }}" + force: true + loop: "{{ account_keys }}" + + vars: + account_keys: + - name: account-ec256 + type: ECC + curve: secp256r1 + - name: account-ec384 + type: ECC + curve: secp384r1 + - name: account-rsa + type: RSA + size: "{{ default_rsa_key_size }}" ## SET UP ACCOUNTS ############################################################################ - name: Make sure ECC256 account hasn't been created yet acme_account: @@ -72,6 +87,7 @@ vars: certgen_title: Certificate 2 certificate_name: cert-2 + certificate_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else '' }}" key_type: ec256 subject_alt_name: "DNS:*.example.com,DNS:example.com" subject_alt_name_critical: yes diff --git a/tests/integration/targets/acme_certificate_revoke/tasks/impl.yml b/tests/integration/targets/acme_certificate_revoke/tasks/impl.yml index cafafa99..0aa1e53e 100644 --- a/tests/integration/targets/acme_certificate_revoke/tasks/impl.yml +++ b/tests/integration/targets/acme_certificate_revoke/tasks/impl.yml @@ -1,11 +1,26 @@ --- ## SET UP ACCOUNT KEYS ######################################################################## -- name: Create ECC256 account key - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem" -- name: Create ECC384 account key - command: "{{ openssl_binary }} ecparam -name secp384r1 -genkey -out {{ output_dir }}/account-ec384.pem" -- name: Create RSA account key - command: "{{ openssl_binary }} genrsa -out {{ output_dir }}/account-rsa.pem {{ default_rsa_key_size }}" +- block: + - name: Generate account keys + openssl_privatekey: + path: "{{ output_dir }}/{{ item.name }}.pem" + type: "{{ item.type }}" + size: "{{ item.size | default(omit) }}" + curve: "{{ item.curve | default(omit) }}" + force: true + loop: "{{ account_keys }}" + + vars: + account_keys: + - name: account-ec256 + type: ECC + curve: secp256r1 + - name: account-ec384 + type: ECC + curve: secp384r1 + - name: account-rsa + type: RSA + size: "{{ default_rsa_key_size }}" ## CREATE ACCOUNTS AND OBTAIN CERTIFICATES #################################################### - name: Obtain cert 1 include_tasks: obtain-cert.yml @@ -29,6 +44,7 @@ vars: certgen_title: Certificate 2 for revocation certificate_name: cert-2 + certificate_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else '' }}" key_type: ec256 subject_alt_name: "DNS:*.example.com" subject_alt_name_critical: yes @@ -71,6 +87,7 @@ acme_certificate_revoke: select_crypto_backend: "{{ select_crypto_backend }}" private_key_src: "{{ output_dir }}/cert-2.key" + private_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}" certificate: "{{ output_dir }}/cert-2.pem" acme_version: 2 acme_directory: https://{{ acme_host }}:14000/dir diff --git a/tests/integration/targets/acme_challenge_cert_helper/tasks/main.yml b/tests/integration/targets/acme_challenge_cert_helper/tasks/main.yml index cd306a4a..81034fbc 100644 --- a/tests/integration/targets/acme_challenge_cert_helper/tasks/main.yml +++ b/tests/integration/targets/acme_challenge_cert_helper/tasks/main.yml @@ -5,8 +5,12 @@ #################################################################### - block: - - name: Create ECC256 account key - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem" + - name: Generate ECC256 accoun keys + openssl_privatekey: + path: "{{ output_dir }}/account-ec256.pem" + type: ECC + curve: secp256r1 + force: true - name: Obtain cert 1 include_tasks: obtain-cert.yml vars: diff --git a/tests/integration/targets/acme_inspect/tasks/impl.yml b/tests/integration/targets/acme_inspect/tasks/impl.yml index 3f5c561f..45b1ea95 100644 --- a/tests/integration/targets/acme_inspect/tasks/impl.yml +++ b/tests/integration/targets/acme_inspect/tasks/impl.yml @@ -1,9 +1,22 @@ --- -- name: Generate account key - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem" +- block: + - name: Generate account keys + openssl_privatekey: + path: "{{ output_dir }}/{{ item }}.pem" + type: ECC + curve: secp256r1 + force: true + loop: "{{ account_keys }}" -- name: Parse account key (to ease debugging some test failures) - command: "{{ openssl_binary }} ec -in {{ output_dir }}/accountkey.pem -noout -text" + - name: Parse account keys (to ease debugging some test failures) + openssl_privatekey_info: + path: "{{ output_dir }}/{{ item }}.pem" + return_private_key_data: true + loop: "{{ account_keys }}" + + vars: + account_keys: + - accountkey - name: Get directory acme_inspect: diff --git a/tests/integration/targets/setup_acme/tasks/obtain-cert.yml b/tests/integration/targets/setup_acme/tasks/obtain-cert.yml index ec53510b..74cf1482 100644 --- a/tests/integration/targets/setup_acme/tasks/obtain-cert.yml +++ b/tests/integration/targets/setup_acme/tasks/obtain-cert.yml @@ -1,22 +1,25 @@ --- ## PRIVATE KEY ################################################################################ -- name: ({{ certgen_title }}) Create cert private key (RSA) - command: "{{ openssl_binary }} genrsa -out {{ output_dir }}/{{ certificate_name }}.key {{ rsa_bits if key_type == 'rsa' else default_rsa_key_size }}" - when: "key_type == 'rsa'" -- name: ({{ certgen_title }}) Create cert private key (ECC 256) - command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/{{ certificate_name }}.key" - when: "key_type == 'ec256'" -- name: ({{ certgen_title }}) Create cert private key (ECC 384) - command: "{{ openssl_binary }} ecparam -name secp384r1 -genkey -out {{ output_dir }}/{{ certificate_name }}.key" - when: "key_type == 'ec384'" -- name: ({{ certgen_title }}) Create cert private key (ECC 512) - command: "{{ openssl_binary }} ecparam -name secp521r1 -genkey -out {{ output_dir }}/{{ certificate_name }}.key" - when: "key_type == 'ec521'" +- name: ({{ certgen_title }}) Create cert private key + openssl_privatekey: + path: "{{ output_dir }}/{{ certificate_name }}.key" + type: "{{ 'RSA' if key_type == 'rsa' else 'ECC' }}" + size: "{{ rsa_bits if key_type == 'rsa' else omit }}" + curve: >- + {{ omit if key_type == 'rsa' else + 'secp256r1' if key_type == 'ec256' else + 'secp384r1' if key_type == 'ec384' else + 'secp521r1' if key_type == 'ec521' else + 'invalid value for key_type!' }} + passphrase: "{{ certificate_passphrase | default(omit, true) }}" + cipher: "{{ 'auto' if certificate_passphrase | default() else omit }}" + force: true ## CSR ######################################################################################## - name: ({{ certgen_title }}) Create cert CSR openssl_csr: path: "{{ output_dir }}/{{ certificate_name }}.csr" privatekey_path: "{{ output_dir }}/{{ certificate_name }}.key" + privatekey_passphrase: "{{ certificate_passphrase | default(omit, true) }}" subject_alt_name: "{{ subject_alt_name }}" subject_alt_name_critical: "{{ subject_alt_name_critical }}" return_content: true @@ -30,6 +33,7 @@ validate_certs: no account_key: "{{ (output_dir ~ '/' ~ account_key ~ '.pem') if account_key_content is not defined else omit }}" account_key_content: "{{ account_key_content | default(omit) }}" + account_key_passphrase: "{{ account_key_passphrase | default(omit, true) }}" modify_account: "{{ modify_account }}" csr: "{{ omit if use_csr_content | default(false) else output_dir ~ '/' ~ certificate_name ~ '.csr' }}" csr_content: "{{ csr_result.csr if use_csr_content | default(false) else omit }}" @@ -69,6 +73,7 @@ challenge: tls-alpn-01 challenge_data: "{{ item.value['tls-alpn-01'] }}" private_key_src: "{{ output_dir }}/{{ certificate_name }}.key" + private_key_passphrase: "{{ certificate_passphrase | default(omit, true) }}" with_dict: "{{ challenge_data.challenge_data }}" register: tls_alpn_challenges when: "challenge_data is changed and challenge == 'tls-alpn-01' and (challenge_alpn_tls is defined and challenge_alpn_tls == 'acme_challenge_cert_helper')" @@ -101,6 +106,7 @@ validate_certs: no account_key: "{{ (output_dir ~ '/' ~ account_key ~ '.pem') if account_key_content is not defined else omit }}" account_key_content: "{{ account_key_content | default(omit) }}" + account_key_passphrase: "{{ account_key_passphrase | default(omit, true) }}" account_uri: "{{ challenge_data.account_uri }}" modify_account: "{{ modify_account }}" csr: "{{ omit if use_csr_content | default(false) else output_dir ~ '/' ~ certificate_name ~ '.csr' }}"