ACI Private_Key String to Allow for Vaulting (#54251)

* Allows the use of Private_Keys to be entered as a string instead of just a file. Making it possible to use VAULT to encrypt the key

* Fixed Issues auto check found

* Provide helpful information while avoiding credential exposure

* Restore original variable name :-)

* Fix a few other things

* Influence the default certificate_name in both cases

* Update documentation

* Add contributed docs

* Fix CI issue
This commit is contained in:
Derrick Johnson
2019-03-27 11:19:50 -05:00
committed by Dag Wieers
parent f9876f3450
commit 62d3ed0e2f
4 changed files with 112 additions and 23 deletions

View File

@@ -69,7 +69,7 @@ def aci_argument_spec():
port=dict(type='int', required=False),
username=dict(type='str', default='admin', aliases=['user']),
password=dict(type='str', no_log=True),
private_key=dict(type='path', aliases=['cert_key']), # Beware, this is not the same as client_key !
private_key=dict(type='str', aliases=['cert_key'], no_log=True), # Beware, this is not the same as client_key !
certificate_name=dict(type='str', aliases=['cert_name']), # Beware, this is not the same as client_cert !
output_level=dict(type='str', default='normal', choices=['debug', 'info', 'normal']),
timeout=dict(type='int', default=30),
@@ -226,16 +226,39 @@ class ACIModule(object):
if payload is None:
payload = ''
# Use the private key basename (without extension) as certificate_name
if self.params['certificate_name'] is None:
self.params['certificate_name'] = os.path.basename(os.path.splitext(self.params['private_key'])[0])
try:
with open(self.params['private_key'], 'r') as priv_key_fh:
private_key_content = priv_key_fh.read()
sig_key = load_privatekey(FILETYPE_PEM, private_key_content)
except Exception:
self.module.fail_json(msg='Cannot load private key %s' % self.params['private_key'])
# Check if we got a private key. This allows the use of vaulting the private key.
if self.params['private_key'].startswith('-----BEGIN PRIVATE KEY-----'):
try:
sig_key = load_privatekey(FILETYPE_PEM, self.params['private_key'])
except Exception:
self.module.fail_json(msg="Cannot load provided 'private_key' parameter.")
# Use the username as the certificate_name value
if self.params['certificate_name'] is None:
self.params['certificate_name'] = self.params['username']
elif self.params['private_key'].startswith('-----BEGIN CERTIFICATE-----'):
self.module.fail_json(msg="Provided 'private_key' parameter value appears to be a certificate. Please correct.")
else:
# If we got a private key file, read from this file.
# NOTE: Avoid exposing any other credential as a filename in output...
if not os.path.exists(self.params['private_key']):
self.module.fail_json(msg="The provided private key file does not appear to exist. Is it a filename?")
try:
with open(self.params['private_key'], 'r') as fh:
private_key_content = fh.read()
except Exception:
self.module.fail_json(msg="Cannot open private key file '%s'." % self.params['private_key'])
if private_key_content.startswith('-----BEGIN PRIVATE KEY-----'):
try:
sig_key = load_privatekey(FILETYPE_PEM, private_key_content)
except Exception:
self.module.fail_json(msg="Cannot load private key file '%s'." % self.params['private_key'])
# Use the private key basename (without extension) as certificate_name
if self.params['certificate_name'] is None:
self.params['certificate_name'] = os.path.basename(os.path.splitext(self.params['private_key'])[0])
elif private_key_content.startswith('-----BEGIN CERTIFICATE-----'):
self.module.fail_json(msg="Provided private key file %s appears to be a certificate. Please correct." % self.params['private_key'])
else:
self.module.fail_json(msg="Provided private key file '%s' does not appear to be a private key. Please correct." % self.params['private_key'])
# NOTE: ACI documentation incorrectly adds a space between method and path
sig_request = method + path + payload
@@ -312,7 +335,7 @@ class ACIModule(object):
self.url = '%(protocol)s://%(host)s/' % self.params + path.lstrip('/')
# Sign and encode request as to APIC's wishes
if self.params['private_key'] is not None:
if not self.params['private_key']:
self.cert_auth(path=path, payload=payload)
# Perform request
@@ -349,7 +372,7 @@ class ACIModule(object):
self.url = '%(protocol)s://%(host)s/' % self.params + path.lstrip('/')
# Sign and encode request as to APIC's wishes
if self.params['private_key'] is not None:
if not self.params['private_key']:
self.cert_auth(path=path, method='GET')
# Perform request
@@ -636,7 +659,7 @@ class ACIModule(object):
elif not self.module.check_mode:
# Sign and encode request as to APIC's wishes
if self.params['private_key'] is not None:
if not self.params['private_key']:
self.cert_auth(method='DELETE')
resp, info = fetch_url(self.module, self.url,
@@ -772,7 +795,7 @@ class ACIModule(object):
uri = self.url + self.filter_string
# Sign and encode request as to APIC's wishes
if self.params['private_key'] is not None:
if not self.params['private_key']:
self.cert_auth(path=self.path + self.filter_string, method='GET')
resp, info = fetch_url(self.module, uri,
@@ -873,7 +896,7 @@ class ACIModule(object):
return
elif not self.module.check_mode:
# Sign and encode request as to APIC's wishes
if self.params['private_key'] is not None:
if not self.params['private_key']:
self.cert_auth(method='POST', payload=json.dumps(self.config))
resp, info = fetch_url(self.module, self.url,

View File

@@ -34,16 +34,17 @@ options:
required: yes
private_key:
description:
- PEM formatted file that contains your private key to be used for signature-based authentication.
- The name of the key (without extension) is used as the certificate name in ACI, unless C(certificate_name) is specified.
- Either a PEM-formatted private key file or the private key content used for signature-based authentication.
- This value also influences the default C(certificate_name) that is used.
- This option is mutual exclusive with C(password). If C(password) is provided too, it will be ignored.
type: path
type: str
required: yes
aliases: [ cert_key ]
certificate_name:
description:
- The X.509 certificate name attached to the APIC AAA user used for signature-based authentication.
- It defaults to the C(private_key) basename, without extension.
- If a C(private_key) filename was provided, this defaults to the C(private_key) basename, without extension.
- If PEM-formatted content was provided for C(private_key), this defaults to the C(username) value.
type: str
aliases: [ cert_name ]
output_level: