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
parent
5d32937321
commit
e85554827f
|
@ -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)."
|
|
@ -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
|
||||||
|
|
|
@ -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]),
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
- name: Generate account keys
|
- block:
|
||||||
command: "{{ openssl_binary }} ecparam -name prime256v1 -genkey -out {{ output_dir }}/{{ item }}.pem"
|
- name: Generate account keys
|
||||||
loop:
|
openssl_privatekey:
|
||||||
- accountkey
|
path: "{{ output_dir }}/{{ item.name }}.pem"
|
||||||
- accountkey2
|
passphrase: "{{ item.pass | default(omit, true) }}"
|
||||||
- accountkey3
|
cipher: "{{ 'auto' if item.pass | default() else omit }}"
|
||||||
- accountkey4
|
type: ECC
|
||||||
- accountkey5
|
curve: secp256r1
|
||||||
|
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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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' }}"
|
||||||
|
|
Loading…
Reference in New Issue