mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
openssl_*: Allow user to specify privatekey passphrase
Allow a user to specify the privatekey passphrase when dealing with openssl modules.
This commit is contained in:
parent
a260063ffd
commit
f40db199aa
4 changed files with 66 additions and 12 deletions
|
@ -26,12 +26,14 @@ except ImportError:
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
def get_fingerprint(path):
|
def get_fingerprint(path, passphrase):
|
||||||
"""Generate the fingerprint of the public key. """
|
"""Generate the fingerprint of the public key. """
|
||||||
|
|
||||||
fingerprint = {}
|
fingerprint = {}
|
||||||
|
|
||||||
privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM, open(path, 'r').read())
|
privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM,
|
||||||
|
open(path, 'rb').read(),
|
||||||
|
passphrase)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
publickey = crypto.dump_publickey(crypto.FILETYPE_ASN1, privatekey)
|
publickey = crypto.dump_publickey(crypto.FILETYPE_ASN1, privatekey)
|
||||||
|
|
|
@ -50,6 +50,11 @@ options:
|
||||||
required: true
|
required: true
|
||||||
description:
|
description:
|
||||||
- Path to the privatekey to use when signing the certificate signing request
|
- Path to the privatekey to use when signing the certificate signing request
|
||||||
|
privatekey_passphrase:
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
- The passphrase for the privatekey.
|
||||||
|
version_added: "2.4"
|
||||||
version:
|
version:
|
||||||
required: false
|
required: false
|
||||||
default: 3
|
default: 3
|
||||||
|
@ -114,6 +119,14 @@ EXAMPLES = '''
|
||||||
privatekey_path: /etc/ssl/private/ansible.com.pem
|
privatekey_path: /etc/ssl/private/ansible.com.pem
|
||||||
commonName: www.ansible.com
|
commonName: www.ansible.com
|
||||||
|
|
||||||
|
# Generate an OpenSSL Certificate Signing Request with a
|
||||||
|
# passphrase protected private key
|
||||||
|
- openssl_csr:
|
||||||
|
path: /etc/ssl/csr/www.ansible.com.csr
|
||||||
|
privatekey_path: /etc/ssl/private/ansible.com.pem
|
||||||
|
privatekey_passphrase: ansible
|
||||||
|
commonName: www.ansible.com
|
||||||
|
|
||||||
# Generate an OpenSSL Certificate Signing Request with Subject information
|
# Generate an OpenSSL Certificate Signing Request with Subject information
|
||||||
- openssl_csr:
|
- openssl_csr:
|
||||||
path: /etc/ssl/csr/www.ansible.com.csr
|
path: /etc/ssl/csr/www.ansible.com.csr
|
||||||
|
@ -183,6 +196,7 @@ class CertificateSigningRequest(object):
|
||||||
self.subjectAltName = module.params['subjectAltName']
|
self.subjectAltName = module.params['subjectAltName']
|
||||||
self.path = module.params['path']
|
self.path = module.params['path']
|
||||||
self.privatekey_path = module.params['privatekey_path']
|
self.privatekey_path = module.params['privatekey_path']
|
||||||
|
self.privatekey_passphrase = module.params['privatekey_passphrase']
|
||||||
self.version = module.params['version']
|
self.version = module.params['version']
|
||||||
self.changed = True
|
self.changed = True
|
||||||
self.request = None
|
self.request = None
|
||||||
|
@ -218,8 +232,9 @@ class CertificateSigningRequest(object):
|
||||||
req.add_extensions([crypto.X509Extension(b"subjectAltName", False, self.subjectAltName.encode('ascii'))])
|
req.add_extensions([crypto.X509Extension(b"subjectAltName", False, self.subjectAltName.encode('ascii'))])
|
||||||
|
|
||||||
privatekey_content = open(self.privatekey_path).read()
|
privatekey_content = open(self.privatekey_path).read()
|
||||||
self.privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM, privatekey_content)
|
self.privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM,
|
||||||
|
privatekey_content,
|
||||||
|
self.privatekey_passphrase)
|
||||||
req.set_pubkey(self.privatekey)
|
req.set_pubkey(self.privatekey)
|
||||||
req.sign(self.privatekey, self.digest)
|
req.sign(self.privatekey, self.digest)
|
||||||
self.request = req
|
self.request = req
|
||||||
|
@ -267,6 +282,7 @@ def main():
|
||||||
state=dict(default='present', choices=['present', 'absent'], type='str'),
|
state=dict(default='present', choices=['present', 'absent'], type='str'),
|
||||||
digest=dict(default='sha256', type='str'),
|
digest=dict(default='sha256', type='str'),
|
||||||
privatekey_path=dict(require=True, type='path'),
|
privatekey_path=dict(require=True, type='path'),
|
||||||
|
privatekey_passphrase=dict(type='str', no_log=True),
|
||||||
version=dict(default='3', type='int'),
|
version=dict(default='3', type='int'),
|
||||||
force=dict(default=False, type='bool'),
|
force=dict(default=False, type='bool'),
|
||||||
subjectAltName=dict(aliases=['subjectAltName'], type='str'),
|
subjectAltName=dict(aliases=['subjectAltName'], type='str'),
|
||||||
|
|
|
@ -62,14 +62,30 @@ options:
|
||||||
required: true
|
required: true
|
||||||
description:
|
description:
|
||||||
- Name of the file in which the generated TLS/SSL private key will be written. It will have 0600 mode.
|
- Name of the file in which the generated TLS/SSL private key will be written. It will have 0600 mode.
|
||||||
|
passphrase:
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
- The passphrase for the private key.
|
||||||
|
version_added: "2.4"
|
||||||
|
cipher:
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
- The cipher to encrypt the private key. (cipher can be found by running `openssl list-cipher-algorithms`)
|
||||||
|
version_added: "2.4"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
# Generate an OpenSSL private key with the default values (4096 bits, RSA)
|
# Generate an OpenSSL private key with the default values (4096 bits, RSA)
|
||||||
# and no public key
|
|
||||||
- openssl_privatekey:
|
- openssl_privatekey:
|
||||||
path: /etc/ssl/private/ansible.com.pem
|
path: /etc/ssl/private/ansible.com.pem
|
||||||
|
|
||||||
|
# Generate an OpenSSL private key with the default values (4096 bits, RSA)
|
||||||
|
# and a passphrase
|
||||||
|
- openssl_privatekey:
|
||||||
|
path: /etc/ssl/private/ansible.com.pem
|
||||||
|
passphrase: ansible
|
||||||
|
cipher: aes256
|
||||||
|
|
||||||
# Generate an OpenSSL private key with a different size (2048 bits)
|
# Generate an OpenSSL private key with a different size (2048 bits)
|
||||||
- openssl_privatekey:
|
- openssl_privatekey:
|
||||||
path: /etc/ssl/private/ansible.com.pem
|
path: /etc/ssl/private/ansible.com.pem
|
||||||
|
@ -145,13 +161,14 @@ class PrivateKey(object):
|
||||||
self.type = module.params['type']
|
self.type = module.params['type']
|
||||||
self.force = module.params['force']
|
self.force = module.params['force']
|
||||||
self.path = module.params['path']
|
self.path = module.params['path']
|
||||||
|
self.passphrase = module.params['passphrase']
|
||||||
|
self.cipher = module.params['cipher']
|
||||||
self.mode = module.params['mode']
|
self.mode = module.params['mode']
|
||||||
self.changed = True
|
self.changed = True
|
||||||
self.privatekey = None
|
self.privatekey = None
|
||||||
self.fingerprint = {}
|
self.fingerprint = {}
|
||||||
self.check_mode = module.check_mode
|
self.check_mode = module.check_mode
|
||||||
|
|
||||||
|
|
||||||
def generate(self, module):
|
def generate(self, module):
|
||||||
"""Generate a keypair."""
|
"""Generate a keypair."""
|
||||||
|
|
||||||
|
@ -173,7 +190,11 @@ class PrivateKey(object):
|
||||||
os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
|
os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
|
||||||
self.mode)
|
self.mode)
|
||||||
|
|
||||||
os.write(privatekey_file, crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey))
|
extras = {}
|
||||||
|
if self.cipher and self.passphrase:
|
||||||
|
extras = {'cipher': self.cipher, 'passphrase': self.passphrase}
|
||||||
|
|
||||||
|
os.write(privatekey_file, crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey, **extras))
|
||||||
os.close(privatekey_file)
|
os.close(privatekey_file)
|
||||||
except IOError as exc:
|
except IOError as exc:
|
||||||
self.remove()
|
self.remove()
|
||||||
|
@ -181,12 +202,11 @@ class PrivateKey(object):
|
||||||
else:
|
else:
|
||||||
self.changed = False
|
self.changed = False
|
||||||
|
|
||||||
self.fingerprint = get_fingerprint(self.path)
|
self.fingerprint = get_fingerprint(self.path, self.passphrase)
|
||||||
file_args = module.load_file_common_arguments(module.params)
|
file_args = module.load_file_common_arguments(module.params)
|
||||||
if module.set_fs_attributes_if_different(file_args, False):
|
if module.set_fs_attributes_if_different(file_args, False):
|
||||||
self.changed = True
|
self.changed = True
|
||||||
|
|
||||||
|
|
||||||
def remove(self):
|
def remove(self):
|
||||||
"""Remove the private key from the filesystem."""
|
"""Remove the private key from the filesystem."""
|
||||||
|
|
||||||
|
@ -198,7 +218,6 @@ class PrivateKey(object):
|
||||||
else:
|
else:
|
||||||
self.changed = False
|
self.changed = False
|
||||||
|
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
"""Serialize the object into a dictionary."""
|
"""Serialize the object into a dictionary."""
|
||||||
|
|
||||||
|
@ -222,9 +241,12 @@ def main():
|
||||||
type=dict(default='RSA', choices=['RSA', 'DSA'], type='str'),
|
type=dict(default='RSA', choices=['RSA', 'DSA'], type='str'),
|
||||||
force=dict(default=False, type='bool'),
|
force=dict(default=False, type='bool'),
|
||||||
path=dict(required=True, type='path'),
|
path=dict(required=True, type='path'),
|
||||||
|
passphrase=dict(type='str', no_log=True),
|
||||||
|
cipher=dict(type='str'),
|
||||||
),
|
),
|
||||||
supports_check_mode = True,
|
supports_check_mode = True,
|
||||||
add_file_common_args = True,
|
add_file_common_args = True,
|
||||||
|
required_together=[['cipher', 'passphrase']],
|
||||||
)
|
)
|
||||||
|
|
||||||
if not pyopenssl_found:
|
if not pyopenssl_found:
|
||||||
|
|
|
@ -62,6 +62,11 @@ options:
|
||||||
required: true
|
required: true
|
||||||
description:
|
description:
|
||||||
- Path to the TLS/SSL private key from which to generate the public key.
|
- Path to the TLS/SSL private key from which to generate the public key.
|
||||||
|
privatekey_passphrase:
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
- The passphrase for the privatekey.
|
||||||
|
version_added: "2.4"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
|
@ -76,6 +81,13 @@ EXAMPLES = '''
|
||||||
privatekey_path: /etc/ssl/private/ansible.com.pem
|
privatekey_path: /etc/ssl/private/ansible.com.pem
|
||||||
format: OpenSSH
|
format: OpenSSH
|
||||||
|
|
||||||
|
# Generate an OpenSSL public key with a passphrase protected
|
||||||
|
# private key
|
||||||
|
- openssl_publickey:
|
||||||
|
path: /etc/ssl/public/ansible.com.pem
|
||||||
|
privatekey_path: /etc/ssl/private/ansible.com.pem
|
||||||
|
privatekey_passphrase: ansible
|
||||||
|
|
||||||
# Force regenerate an OpenSSL public key if it already exists
|
# Force regenerate an OpenSSL public key if it already exists
|
||||||
- openssl_publickey:
|
- openssl_publickey:
|
||||||
path: /etc/ssl/public/ansible.com.pem
|
path: /etc/ssl/public/ansible.com.pem
|
||||||
|
@ -150,6 +162,7 @@ class PublicKey(object):
|
||||||
self.name = os.path.basename(module.params['path'])
|
self.name = os.path.basename(module.params['path'])
|
||||||
self.path = module.params['path']
|
self.path = module.params['path']
|
||||||
self.privatekey_path = module.params['privatekey_path']
|
self.privatekey_path = module.params['privatekey_path']
|
||||||
|
self.privatekey_passphrase = module.params['privatekey_passphrase']
|
||||||
self.privatekey = None
|
self.privatekey = None
|
||||||
self.changed = True
|
self.changed = True
|
||||||
self.fingerprint = {}
|
self.fingerprint = {}
|
||||||
|
@ -164,7 +177,7 @@ class PublicKey(object):
|
||||||
|
|
||||||
if self.format == 'OpenSSH':
|
if self.format == 'OpenSSH':
|
||||||
key = crypto_serialization.load_pem_private_key(privatekey_content,
|
key = crypto_serialization.load_pem_private_key(privatekey_content,
|
||||||
password=None,
|
password=self.privatekey_passphrase,
|
||||||
backend=default_backend())
|
backend=default_backend())
|
||||||
publickey_content = key.public_key().public_bytes(
|
publickey_content = key.public_key().public_bytes(
|
||||||
crypto_serialization.Encoding.OpenSSH,
|
crypto_serialization.Encoding.OpenSSH,
|
||||||
|
@ -190,7 +203,7 @@ class PublicKey(object):
|
||||||
self.changed = False
|
self.changed = False
|
||||||
|
|
||||||
file_args = module.load_file_common_arguments(module.params)
|
file_args = module.load_file_common_arguments(module.params)
|
||||||
self.fingerprint = get_fingerprint(self.privatekey_path)
|
self.fingerprint = get_fingerprint(self.privatekey_path, self.privatekey_passphrase)
|
||||||
if module.set_fs_attributes_if_different(file_args, False):
|
if module.set_fs_attributes_if_different(file_args, False):
|
||||||
self.changed = True
|
self.changed = True
|
||||||
|
|
||||||
|
@ -228,6 +241,7 @@ def main():
|
||||||
path=dict(required=True, type='path'),
|
path=dict(required=True, type='path'),
|
||||||
privatekey_path=dict(type='path'),
|
privatekey_path=dict(type='path'),
|
||||||
format=dict(type='str', choices=['PEM', 'OpenSSH'], default='PEM'),
|
format=dict(type='str', choices=['PEM', 'OpenSSH'], default='PEM'),
|
||||||
|
privatekey_passphrase=dict(type='path', no_log=True),
|
||||||
),
|
),
|
||||||
supports_check_mode = True,
|
supports_check_mode = True,
|
||||||
add_file_common_args = True,
|
add_file_common_args = True,
|
||||||
|
|
Loading…
Reference in a new issue