acme_* modules: support private key passprases (#207)

* Support private key passprases.

* Use c.c modules for key generation, add first passphrase tests.

* Some more passphrase tests.
pull/208/head
Felix Fontein 2021-03-21 17:53:20 +01:00 committed by GitHub
parent 5d32937321
commit e85554827f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 190 additions and 64 deletions

View File

@ -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)."

View File

@ -57,6 +57,12 @@ options:
Ansible in the process of moving the module with its argument to Ansible in the process of moving the module with its argument to
the node where it is executed." the node where it is executed."
type: str 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: account_uri:
description: description:
- "If specified, assumes that the account URI is as given. If the - "If specified, assumes that the account URI is as given. If the

View File

@ -112,6 +112,7 @@ class ACMEClient(object):
# account_key path and content are mutually exclusive # account_key path and content are mutually exclusive
self.account_key_file = module.params['account_key_src'] self.account_key_file = module.params['account_key_src']
self.account_key_content = module.params['account_key_content'] self.account_key_content = module.params['account_key_content']
self.account_key_passphrase = module.params['account_key_passphrase']
# Grab account URI from module parameters. # Grab account URI from module parameters.
# Make sure empty string is treated as None. # Make sure empty string is treated as None.
@ -122,7 +123,10 @@ class ACMEClient(object):
self.account_jws_header = None self.account_jws_header = None
if self.account_key_file is not None or self.account_key_content is not None: if self.account_key_file is not None or self.account_key_content is not None:
try: 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: except KeyParsingError as e:
raise ModuleFailException("Error while parsing account key: {msg}".format(msg=e.msg)) raise ModuleFailException("Error while parsing account key: {msg}".format(msg=e.msg))
self.account_jwk = self.account_key_data['jwk'] 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.pop('jwk')
self.account_jws_header['kid'] = self.account_uri 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. Parses an RSA or Elliptic Curve key file in PEM format and returns key_data.
In case of an error, raises KeyParsingError. In case of an error, raises KeyParsingError.
''' '''
if key_file is None and key_content is None: if key_file is None and key_content is None:
raise AssertionError('One of key_file and key_content must be specified!') 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: if error:
raise KeyParsingError(error) raise KeyParsingError(error)
return key_data return key_data
@ -311,6 +315,7 @@ def get_default_argspec():
return dict( return dict(
account_key_src=dict(type='path', aliases=['account_key']), account_key_src=dict(type='path', aliases=['account_key']),
account_key_content=dict(type='str', no_log=True), account_key_content=dict(type='str', no_log=True),
account_key_passphrase=dict(type='str', no_log=True),
account_uri=dict(type='str'), account_uri=dict(type='str'),
acme_directory=dict(type='str'), acme_directory=dict(type='str'),
acme_version=dict(type='int', choices=[1, 2]), acme_version=dict(type='int', choices=[1, 2]),

View File

@ -179,7 +179,7 @@ class CryptographyBackend(CryptoBackend):
def __init__(self, module): def __init__(self, module):
super(CryptographyBackend, self).__init__(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 Parses an RSA or Elliptic Curve key file in PEM format and returns a pair
(error, key_data). (error, key_data).
@ -191,7 +191,10 @@ class CryptographyBackend(CryptoBackend):
key_content = to_bytes(key_content) key_content = to_bytes(key_content)
# Parse key # Parse key
try: 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: except Exception as e:
return 'error while loading key: {0}'.format(e), None return 'error while loading key: {0}'.format(e), None
if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey): if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):

View File

@ -41,11 +41,13 @@ class OpenSSLCLIBackend(CryptoBackend):
openssl_binary = module.get_bin_path('openssl', True) openssl_binary = module.get_bin_path('openssl', True)
self.openssl_binary = openssl_binary 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 Parses an RSA or Elliptic Curve key file in PEM format and returns a pair
(error, key_data). (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 isn't given, but key_content, write that to a temporary file
if key_file is None: if key_file is None:
fd, tmpsrc = tempfile.mkstemp() fd, tmpsrc = tempfile.mkstemp()

View File

@ -19,7 +19,7 @@ class CryptoBackend(object):
self.module = module self.module = module
@abc.abstractmethod @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 Parses an RSA or Elliptic Curve key file in PEM format and returns a pair
(error, key_data). (error, key_data).

View File

@ -88,6 +88,12 @@ options:
- "Mutually exclusive with C(new_account_key_src)." - "Mutually exclusive with C(new_account_key_src)."
- "Required if C(new_account_key_src) is not used and state is C(changed_key)." - "Required if C(new_account_key_src) is not used and state is C(changed_key)."
type: str 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: external_account_binding:
description: description:
- Allows to provide external account binding data during account creation. - Allows to provide external account binding data during account creation.
@ -183,6 +189,7 @@ def main():
contact=dict(type='list', elements='str', default=[]), contact=dict(type='list', elements='str', default=[]),
new_account_key_src=dict(type='path'), new_account_key_src=dict(type='path'),
new_account_key_content=dict(type='str', no_log=True), 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( external_account_binding=dict(type='dict', options=dict(
kid=dict(type='str', required=True), kid=dict(type='str', required=True),
alg=dict(type='str', required=True, choices=['HS256', 'HS384', 'HS512']), alg=dict(type='str', required=True, choices=['HS256', 'HS384', 'HS512']),
@ -272,7 +279,8 @@ def main():
try: try:
new_key_data = client.parse_key( new_key_data = client.parse_key(
module.params.get('new_account_key_src'), 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: except KeyParsingError as e:
raise ModuleFailException("Error while parsing new account key: {msg}".format(msg=e.msg)) raise ModuleFailException("Error while parsing new account key: {msg}".format(msg=e.msg))

View File

@ -54,7 +54,6 @@ options:
private keys in PEM format can be used as well." private keys in PEM format can be used as well."
- "Mutually exclusive with C(account_key_content)." - "Mutually exclusive with C(account_key_content)."
- "Required if C(account_key_content) is not used." - "Required if C(account_key_content) is not used."
type: path
account_key_content: account_key_content:
description: description:
- "Content of the ACME account RSA or Elliptic Curve key." - "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 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 Ansible in the process of moving the module with its argument to
the node where it is executed." the node where it is executed."
type: str
private_key_src: private_key_src:
description: description:
- "Path to the certificate's private key." - "Path to the certificate's private key."
@ -91,6 +89,12 @@ options:
Ansible in the process of moving the module with its argument to Ansible in the process of moving the module with its argument to
the node where it is executed." the node where it is executed."
type: str 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: revoke_reason:
description: description:
- "One of the revocation reasonCodes defined in - "One of the revocation reasonCodes defined in
@ -146,6 +150,7 @@ def main():
argument_spec.update(dict( argument_spec.update(dict(
private_key_src=dict(type='path'), private_key_src=dict(type='path'),
private_key_content=dict(type='str', no_log=True), private_key_content=dict(type='str', no_log=True),
private_key_passphrase=dict(type='str', no_log=True),
certificate=dict(type='path', required=True), certificate=dict(type='path', required=True),
revoke_reason=dict(type='int'), revoke_reason=dict(type='int'),
)) ))
@ -184,9 +189,10 @@ def main():
private_key_content = module.params.get('private_key_content') private_key_content = module.params.get('private_key_content')
# Revoke certificate # Revoke certificate
if private_key or private_key_content: if private_key or private_key_content:
passphrase = module.params['private_key_passphrase']
# Step 1: load and parse private key # Step 1: load and parse private key
try: 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: except KeyParsingError as e:
raise ModuleFailException("Error while parsing private key: {msg}".format(msg=e.msg)) raise ModuleFailException("Error while parsing private key: {msg}".format(msg=e.msg))
# Step 2: sign revokation request with private key # Step 2: sign revokation request with private key

View File

@ -52,6 +52,11 @@ options:
- "Content of the private key to use for this challenge certificate." - "Content of the private key to use for this challenge certificate."
- "Mutually exclusive with C(private_key_src)." - "Mutually exclusive with C(private_key_src)."
type: str type: str
private_key_passphrase:
description:
- Phassphrase to use to decode the private key.
type: str
version_added: 1.6.0
notes: notes:
- Does not support C(check_mode). - Does not support C(check_mode).
''' '''
@ -187,6 +192,7 @@ def main():
challenge_data=dict(type='dict', required=True), challenge_data=dict(type='dict', required=True),
private_key_src=dict(type='path'), private_key_src=dict(type='path'),
private_key_content=dict(type='str', no_log=True), private_key_content=dict(type='str', no_log=True),
private_key_passphrase=dict(type='str', no_log=True),
), ),
required_one_of=( required_one_of=(
['private_key_src', 'private_key_content'], ['private_key_src', 'private_key_content'],
@ -205,12 +211,16 @@ def main():
# Get hold of private key # Get hold of private key
private_key_content = module.params.get('private_key_content') private_key_content = module.params.get('private_key_content')
private_key_passphrase = module.params.get('private_key_passphrase')
if private_key_content is None: if private_key_content is None:
private_key_content = read_file(module.params['private_key_src']) private_key_content = read_file(module.params['private_key_src'])
else: else:
private_key_content = to_bytes(private_key_content) private_key_content = to_bytes(private_key_content)
try: 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: except Exception as e:
raise ModuleFailException('Error while loading private key: {0}'.format(e)) raise ModuleFailException('Error while loading private key: {0}'.format(e))

View File

@ -1,20 +1,29 @@
- block:
- name: Generate account keys - name: Generate account keys
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/{{ item }}.pem" openssl_privatekey:
loop: path: "{{ output_dir }}/{{ item.name }}.pem"
- accountkey passphrase: "{{ item.pass | default(omit, true) }}"
- accountkey2 cipher: "{{ 'auto' if item.pass | default() else omit }}"
- accountkey3 type: ECC
- accountkey4 curve: secp256r1
- accountkey5 force: true
loop: "{{ account_keys }}"
- name: Parse account keys (to ease debugging some test failures) - name: Parse account keys (to ease debugging some test failures)
command: "{{ openssl_binary }} ec -in {{ output_dir }}/{{ item }}.pem -noout -text" openssl_privatekey_info:
loop: path: "{{ output_dir }}/{{ item.name }}.pem"
- accountkey passphrase: "{{ item.pass | default(omit, true) }}"
- accountkey2 return_private_key_data: true
- accountkey3 loop: "{{ account_keys }}"
- accountkey4
- accountkey5 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 - name: Do not try to create account
acme_account: acme_account:
@ -173,6 +182,7 @@
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir
validate_certs: no validate_certs: no
new_account_key_src: "{{ output_dir }}/accountkey2.pem" new_account_key_src: "{{ output_dir }}/accountkey2.pem"
new_account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}"
state: changed_key state: changed_key
contact: contact:
- mailto:example@example.com - mailto:example@example.com
@ -188,6 +198,7 @@
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir
validate_certs: no validate_certs: no
new_account_key_src: "{{ output_dir }}/accountkey2.pem" new_account_key_src: "{{ output_dir }}/accountkey2.pem"
new_account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}"
state: changed_key state: changed_key
contact: contact:
- mailto:example@example.com - mailto:example@example.com
@ -197,6 +208,7 @@
acme_account: acme_account:
select_crypto_backend: "{{ select_crypto_backend }}" select_crypto_backend: "{{ select_crypto_backend }}"
account_key_src: "{{ output_dir }}/accountkey2.pem" account_key_src: "{{ output_dir }}/accountkey2.pem"
account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}"
acme_version: 2 acme_version: 2
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir
validate_certs: no validate_certs: no
@ -209,6 +221,7 @@
acme_account: acme_account:
select_crypto_backend: "{{ select_crypto_backend }}" select_crypto_backend: "{{ select_crypto_backend }}"
account_key_src: "{{ output_dir }}/accountkey2.pem" account_key_src: "{{ output_dir }}/accountkey2.pem"
account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}"
acme_version: 2 acme_version: 2
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir
validate_certs: no validate_certs: no
@ -219,6 +232,7 @@
acme_account: acme_account:
select_crypto_backend: "{{ select_crypto_backend }}" select_crypto_backend: "{{ select_crypto_backend }}"
account_key_src: "{{ output_dir }}/accountkey2.pem" account_key_src: "{{ output_dir }}/accountkey2.pem"
account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}"
acme_version: 2 acme_version: 2
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir
validate_certs: no validate_certs: no
@ -229,6 +243,7 @@
acme_account: acme_account:
select_crypto_backend: "{{ select_crypto_backend }}" select_crypto_backend: "{{ select_crypto_backend }}"
account_key_src: "{{ output_dir }}/accountkey2.pem" account_key_src: "{{ output_dir }}/accountkey2.pem"
account_key_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else omit }}"
acme_version: 2 acme_version: 2
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir
validate_certs: no validate_certs: no

View File

@ -1,12 +1,23 @@
--- ---
- name: Generate account key - block:
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem" - 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 - name: Parse account keys (to ease debugging some test failures)
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey2.pem" 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) vars:
command: "{{ openssl_binary }} ec -in {{ output_dir }}/accountkey.pem -noout -text" account_keys:
- accountkey
- accountkey2
- name: Check that account does not exist - name: Check that account does not exist
acme_account_info: acme_account_info:

View File

@ -1,11 +1,26 @@
--- ---
## SET UP ACCOUNT KEYS ######################################################################## ## SET UP ACCOUNT KEYS ########################################################################
- name: Create ECC256 account key - block:
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem" - name: Generate account keys
- name: Create ECC384 account key openssl_privatekey:
command: "{{ openssl_binary }} ecparam -name secp384r1 -genkey -out {{ output_dir }}/account-ec384.pem" path: "{{ output_dir }}/{{ item.name }}.pem"
- name: Create RSA account key type: "{{ item.type }}"
command: "{{ openssl_binary }} genrsa -out {{ output_dir }}/account-rsa.pem {{ default_rsa_key_size }}" 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 ############################################################################ ## SET UP ACCOUNTS ############################################################################
- name: Make sure ECC256 account hasn't been created yet - name: Make sure ECC256 account hasn't been created yet
acme_account: acme_account:
@ -72,6 +87,7 @@
vars: vars:
certgen_title: Certificate 2 certgen_title: Certificate 2
certificate_name: cert-2 certificate_name: cert-2
certificate_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else '' }}"
key_type: ec256 key_type: ec256
subject_alt_name: "DNS:*.example.com,DNS:example.com" subject_alt_name: "DNS:*.example.com,DNS:example.com"
subject_alt_name_critical: yes subject_alt_name_critical: yes

View File

@ -1,11 +1,26 @@
--- ---
## SET UP ACCOUNT KEYS ######################################################################## ## SET UP ACCOUNT KEYS ########################################################################
- name: Create ECC256 account key - block:
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem" - name: Generate account keys
- name: Create ECC384 account key openssl_privatekey:
command: "{{ openssl_binary }} ecparam -name secp384r1 -genkey -out {{ output_dir }}/account-ec384.pem" path: "{{ output_dir }}/{{ item.name }}.pem"
- name: Create RSA account key type: "{{ item.type }}"
command: "{{ openssl_binary }} genrsa -out {{ output_dir }}/account-rsa.pem {{ default_rsa_key_size }}" 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 #################################################### ## CREATE ACCOUNTS AND OBTAIN CERTIFICATES ####################################################
- name: Obtain cert 1 - name: Obtain cert 1
include_tasks: obtain-cert.yml include_tasks: obtain-cert.yml
@ -29,6 +44,7 @@
vars: vars:
certgen_title: Certificate 2 for revocation certgen_title: Certificate 2 for revocation
certificate_name: cert-2 certificate_name: cert-2
certificate_passphrase: "{{ 'hunter2' if select_crypto_backend != 'openssl' else '' }}"
key_type: ec256 key_type: ec256
subject_alt_name: "DNS:*.example.com" subject_alt_name: "DNS:*.example.com"
subject_alt_name_critical: yes subject_alt_name_critical: yes
@ -71,6 +87,7 @@
acme_certificate_revoke: acme_certificate_revoke:
select_crypto_backend: "{{ select_crypto_backend }}" select_crypto_backend: "{{ select_crypto_backend }}"
private_key_src: "{{ output_dir }}/cert-2.key" 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" certificate: "{{ output_dir }}/cert-2.pem"
acme_version: 2 acme_version: 2
acme_directory: https://{{ acme_host }}:14000/dir acme_directory: https://{{ acme_host }}:14000/dir

View File

@ -5,8 +5,12 @@
#################################################################### ####################################################################
- block: - block:
- name: Create ECC256 account key - name: Generate ECC256 accoun keys
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem" openssl_privatekey:
path: "{{ output_dir }}/account-ec256.pem"
type: ECC
curve: secp256r1
force: true
- name: Obtain cert 1 - name: Obtain cert 1
include_tasks: obtain-cert.yml include_tasks: obtain-cert.yml
vars: vars:

View File

@ -1,9 +1,22 @@
--- ---
- name: Generate account key - block:
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem" - 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) - name: Parse account keys (to ease debugging some test failures)
command: "{{ openssl_binary }} ec -in {{ output_dir }}/accountkey.pem -noout -text" openssl_privatekey_info:
path: "{{ output_dir }}/{{ item }}.pem"
return_private_key_data: true
loop: "{{ account_keys }}"
vars:
account_keys:
- accountkey
- name: Get directory - name: Get directory
acme_inspect: acme_inspect:

View File

@ -1,22 +1,25 @@
--- ---
## PRIVATE KEY ################################################################################ ## PRIVATE KEY ################################################################################
- name: ({{ certgen_title }}) Create cert private key (RSA) - name: ({{ certgen_title }}) Create cert private key
command: "{{ openssl_binary }} genrsa -out {{ output_dir }}/{{ certificate_name }}.key {{ rsa_bits if key_type == 'rsa' else default_rsa_key_size }}" openssl_privatekey:
when: "key_type == 'rsa'" path: "{{ output_dir }}/{{ certificate_name }}.key"
- name: ({{ certgen_title }}) Create cert private key (ECC 256) type: "{{ 'RSA' if key_type == 'rsa' else 'ECC' }}"
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/{{ certificate_name }}.key" size: "{{ rsa_bits if key_type == 'rsa' else omit }}"
when: "key_type == 'ec256'" curve: >-
- name: ({{ certgen_title }}) Create cert private key (ECC 384) {{ omit if key_type == 'rsa' else
command: "{{ openssl_binary }} ecparam -name secp384r1 -genkey -out {{ output_dir }}/{{ certificate_name }}.key" 'secp256r1' if key_type == 'ec256' else
when: "key_type == 'ec384'" 'secp384r1' if key_type == 'ec384' else
- name: ({{ certgen_title }}) Create cert private key (ECC 512) 'secp521r1' if key_type == 'ec521' else
command: "{{ openssl_binary }} ecparam -name secp521r1 -genkey -out {{ output_dir }}/{{ certificate_name }}.key" 'invalid value for key_type!' }}
when: "key_type == 'ec521'" passphrase: "{{ certificate_passphrase | default(omit, true) }}"
cipher: "{{ 'auto' if certificate_passphrase | default() else omit }}"
force: true
## CSR ######################################################################################## ## CSR ########################################################################################
- name: ({{ certgen_title }}) Create cert CSR - name: ({{ certgen_title }}) Create cert CSR
openssl_csr: openssl_csr:
path: "{{ output_dir }}/{{ certificate_name }}.csr" path: "{{ output_dir }}/{{ certificate_name }}.csr"
privatekey_path: "{{ output_dir }}/{{ certificate_name }}.key" privatekey_path: "{{ output_dir }}/{{ certificate_name }}.key"
privatekey_passphrase: "{{ certificate_passphrase | default(omit, true) }}"
subject_alt_name: "{{ subject_alt_name }}" subject_alt_name: "{{ subject_alt_name }}"
subject_alt_name_critical: "{{ subject_alt_name_critical }}" subject_alt_name_critical: "{{ subject_alt_name_critical }}"
return_content: true return_content: true
@ -30,6 +33,7 @@
validate_certs: no validate_certs: no
account_key: "{{ (output_dir ~ '/' ~ account_key ~ '.pem') if account_key_content is not defined else omit }}" 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_content: "{{ account_key_content | default(omit) }}"
account_key_passphrase: "{{ account_key_passphrase | default(omit, true) }}"
modify_account: "{{ modify_account }}" modify_account: "{{ modify_account }}"
csr: "{{ omit if use_csr_content | default(false) else output_dir ~ '/' ~ certificate_name ~ '.csr' }}" 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 }}" csr_content: "{{ csr_result.csr if use_csr_content | default(false) else omit }}"
@ -69,6 +73,7 @@
challenge: tls-alpn-01 challenge: tls-alpn-01
challenge_data: "{{ item.value['tls-alpn-01'] }}" challenge_data: "{{ item.value['tls-alpn-01'] }}"
private_key_src: "{{ output_dir }}/{{ certificate_name }}.key" private_key_src: "{{ output_dir }}/{{ certificate_name }}.key"
private_key_passphrase: "{{ certificate_passphrase | default(omit, true) }}"
with_dict: "{{ challenge_data.challenge_data }}" with_dict: "{{ challenge_data.challenge_data }}"
register: tls_alpn_challenges 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')" 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 validate_certs: no
account_key: "{{ (output_dir ~ '/' ~ account_key ~ '.pem') if account_key_content is not defined else omit }}" 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_content: "{{ account_key_content | default(omit) }}"
account_key_passphrase: "{{ account_key_passphrase | default(omit, true) }}"
account_uri: "{{ challenge_data.account_uri }}" account_uri: "{{ challenge_data.account_uri }}"
modify_account: "{{ modify_account }}" modify_account: "{{ modify_account }}"
csr: "{{ omit if use_csr_content | default(false) else output_dir ~ '/' ~ certificate_name ~ '.csr' }}" csr: "{{ omit if use_csr_content | default(false) else output_dir ~ '/' ~ certificate_name ~ '.csr' }}"