Refactors bigip ssl cert module (#25882)
Includes unit tests and a refactor of the module to make code more easily supported. Also fixes an issue where the extension of the cert name was not appended if not provided. This problem was causing certs to not be editable in the UI. Also deprecates params. Unit tests are provided. Integration tests can be found here https://github.com/F5Networks/f5-ansible/blob/devel/test/integration/bigip_ssl_certificate.yaml#L23 https://github.com/F5Networks/f5-ansible/tree/devel/test/integration/targets/bigip_ssl_certificate/tasks
This commit is contained in:
parent
7870000f71
commit
16bd93a14e
8 changed files with 1162 additions and 336 deletions
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# (c) 2016, Kevin Coming (@waffie1)
|
||||
# Copyright 2017 F5 Networks Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
|
@ -17,14 +17,15 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community',
|
||||
'metadata_version': '1.0'
|
||||
}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: bigip_ssl_certificate
|
||||
short_description: Import/Delete certificates from BIG-IP
|
||||
short_description: Import/Delete certificates from BIG-IP.
|
||||
description:
|
||||
- This module will import/delete SSL certificates on BIG-IP LTM.
|
||||
Certificates can be imported from certificate and key files on the local
|
||||
|
@ -38,7 +39,6 @@ options:
|
|||
with formatting or templating. Either one of C(key_src),
|
||||
C(key_content), C(cert_src) or C(cert_content) must be provided when
|
||||
C(state) is C(present).
|
||||
required: false
|
||||
key_content:
|
||||
description:
|
||||
- When used instead of 'key_src', sets the contents of a certificate key
|
||||
|
@ -46,57 +46,47 @@ options:
|
|||
anything with formatting or templating. Either one of C(key_src),
|
||||
C(key_content), C(cert_src) or C(cert_content) must be provided when
|
||||
C(state) is C(present).
|
||||
required: false
|
||||
state:
|
||||
description:
|
||||
- Certificate and key state. This determines if the provided certificate
|
||||
and key is to be made C(present) on the device or C(absent).
|
||||
required: true
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
partition:
|
||||
description:
|
||||
- BIG-IP partition to use when adding/deleting certificate.
|
||||
required: false
|
||||
default: Common
|
||||
name:
|
||||
description:
|
||||
- SSL Certificate Name. This is the cert/key pair name used
|
||||
when importing a certificate/key into the F5. It also
|
||||
determines the filenames of the objects on the LTM
|
||||
(:Partition:name.cer_11111_1 and :Partition_name.key_11111_1).
|
||||
required: true
|
||||
required: True
|
||||
cert_src:
|
||||
description:
|
||||
- This is the local filename of the certificate. Either one of C(key_src),
|
||||
C(key_content), C(cert_src) or C(cert_content) must be provided when
|
||||
C(state) is C(present).
|
||||
required: false
|
||||
key_src:
|
||||
description:
|
||||
- This is the local filename of the private key. Either one of C(key_src),
|
||||
C(key_content), C(cert_src) or C(cert_content) must be provided when
|
||||
C(state) is C(present).
|
||||
required: false
|
||||
passphrase:
|
||||
description:
|
||||
- Passphrase on certificate private key
|
||||
required: false
|
||||
notes:
|
||||
- Requires the f5-sdk Python package on the host. This is as easy as pip
|
||||
install f5-sdk.
|
||||
- Requires the netaddr Python package on the host.
|
||||
- If you use this module, you will not be able to remove the certificates
|
||||
and keys that are managed, via the web UI. You can only remove them via
|
||||
tmsh or these modules.
|
||||
- This module does not behave like other modules that you might include in
|
||||
roles where referencing files or templates first looks in the role's
|
||||
files or templates directory. To have it behave that way, use the Ansible
|
||||
file or template lookup (see Examples). The lookups behave as expected in
|
||||
a role context.
|
||||
extends_documentation_fragment: f5
|
||||
requirements:
|
||||
- f5-sdk >= 1.5.0
|
||||
- BigIP >= v12
|
||||
- BIG-IP >= v12
|
||||
author:
|
||||
- Kevin Coming (@waffie1)
|
||||
- Tim Rupp (@caphrim007)
|
||||
'''
|
||||
|
||||
|
@ -135,279 +125,263 @@ EXAMPLES = '''
|
|||
|
||||
RETURN = '''
|
||||
cert_name:
|
||||
description: >
|
||||
The name of the SSL certificate. The C(cert_name) and
|
||||
C(key_name) will be equal to each other.
|
||||
returned: created, changed or deleted
|
||||
description: The name of the certificate that the user provided
|
||||
returned: created
|
||||
type: string
|
||||
sample: "cert1"
|
||||
key_name:
|
||||
description: >
|
||||
The name of the SSL certificate key. The C(key_name) and
|
||||
C(cert_name) will be equal to each other.
|
||||
returned: created, changed or deleted
|
||||
key_filename:
|
||||
description:
|
||||
- The name of the SSL certificate key. The C(key_filename) and
|
||||
C(cert_filename) will be similar to each other, however the
|
||||
C(key_filename) will have a C(.key) extension.
|
||||
returned: created
|
||||
type: string
|
||||
sample: "key1"
|
||||
partition:
|
||||
description: Partition in which the cert/key was created
|
||||
returned: created, changed or deleted
|
||||
type: string
|
||||
sample: "Common"
|
||||
sample: "cert1.key"
|
||||
key_checksum:
|
||||
description: SHA1 checksum of the key that was provided
|
||||
returned: created or changed
|
||||
description: SHA1 checksum of the key that was provided.
|
||||
returned: changed and created
|
||||
type: string
|
||||
sample: "cf23df2207d99a74fbe169e3eba035e633b65d94"
|
||||
key_source_path:
|
||||
description: Path on BIG-IP where the source of the key is stored
|
||||
returned: created
|
||||
type: string
|
||||
sample: "/var/config/rest/downloads/cert1.key"
|
||||
cert_filename:
|
||||
description:
|
||||
- The name of the SSL certificate. The C(cert_filename) and
|
||||
C(key_filename) will be similar to each other, however the
|
||||
C(cert_filename) will have a C(.crt) extension.
|
||||
returned: created
|
||||
type: string
|
||||
sample: "cert1.crt"
|
||||
cert_checksum:
|
||||
description: SHA1 checksum of the cert that was provided
|
||||
returned: created or changed
|
||||
description: SHA1 checksum of the cert that was provided.
|
||||
returned: changed and created
|
||||
type: string
|
||||
sample: "f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0"
|
||||
cert_source_path:
|
||||
description: Path on BIG-IP where the source of the certificate is stored.
|
||||
returned: created
|
||||
type: string
|
||||
sample: "/var/config/rest/downloads/cert1.crt"
|
||||
'''
|
||||
|
||||
|
||||
try:
|
||||
from f5.bigip.contexts import TransactionContextManager
|
||||
from f5.bigip import ManagementRoot
|
||||
from icontrol.session import iControlUnexpectedHTTPError
|
||||
HAS_F5SDK = True
|
||||
except ImportError:
|
||||
HAS_F5SDK = False
|
||||
|
||||
|
||||
import hashlib
|
||||
import StringIO
|
||||
import os
|
||||
import re
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
from ansible.module_utils.f5_utils import (
|
||||
AnsibleF5Client,
|
||||
AnsibleF5Parameters,
|
||||
HAS_F5SDK,
|
||||
F5ModuleError,
|
||||
iControlUnexpectedHTTPError,
|
||||
iteritems
|
||||
)
|
||||
|
||||
|
||||
class BigIpSslCertificate(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if not HAS_F5SDK:
|
||||
raise F5ModuleError("The python f5-sdk module is required")
|
||||
class Parameters(AnsibleF5Parameters):
|
||||
def __init__(self, params=None):
|
||||
super(Parameters, self).__init__(params)
|
||||
self._values['__warnings'] = []
|
||||
|
||||
required_args = ['key_content', 'key_src', 'cert_content', 'cert_src']
|
||||
def to_return(self):
|
||||
result = {}
|
||||
try:
|
||||
for returnable in self.returnables:
|
||||
result[returnable] = getattr(self, returnable)
|
||||
result = self._filter_params(result)
|
||||
except Exception:
|
||||
pass
|
||||
return result
|
||||
|
||||
ksource = kwargs['key_src']
|
||||
if ksource:
|
||||
with open(ksource) as f:
|
||||
kwargs['key_content'] = f.read()
|
||||
def api_params(self):
|
||||
result = {}
|
||||
for api_attribute in self.api_attributes:
|
||||
if self.api_map is not None and api_attribute in self.api_map:
|
||||
result[api_attribute] = getattr(self, self.api_map[api_attribute])
|
||||
else:
|
||||
result[api_attribute] = getattr(self, api_attribute)
|
||||
result = self._filter_params(result)
|
||||
return result
|
||||
|
||||
csource = kwargs['cert_src']
|
||||
if csource:
|
||||
with open(csource) as f:
|
||||
kwargs['cert_content'] = f.read()
|
||||
|
||||
if kwargs['state'] == 'present':
|
||||
if not any(kwargs[k] is not None for k in required_args):
|
||||
raise F5ModuleError(
|
||||
"Either 'key_content', 'key_src', 'cert_content' or "
|
||||
"'cert_src' must be provided"
|
||||
)
|
||||
|
||||
# This is the remote BIG-IP path from where it will look for certs
|
||||
# to install.
|
||||
self.dlpath = '/var/config/rest/downloads'
|
||||
|
||||
# The params that change in the module
|
||||
self.cparams = dict()
|
||||
|
||||
# Stores the params that are sent to the module
|
||||
self.params = kwargs
|
||||
self.api = ManagementRoot(kwargs['server'],
|
||||
kwargs['user'],
|
||||
kwargs['password'],
|
||||
port=kwargs['server_port'])
|
||||
|
||||
def exists(self):
|
||||
cert = self.cert_exists()
|
||||
key = self.key_exists()
|
||||
|
||||
if cert and key:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_hash(self, content):
|
||||
def _get_hash(self, content):
|
||||
k = hashlib.sha1()
|
||||
s = StringIO.StringIO(content)
|
||||
s = StringIO(content)
|
||||
while True:
|
||||
data = s.read(1024)
|
||||
if not data:
|
||||
break
|
||||
k.update(data)
|
||||
k.update(data.encode('utf-8'))
|
||||
return k.hexdigest()
|
||||
|
||||
def present(self):
|
||||
current = self.read()
|
||||
@property
|
||||
def checksum(self):
|
||||
if self._values['checksum'] is None:
|
||||
return None
|
||||
pattern = r'SHA1:\d+:(?P<value>[\w+]{40})'
|
||||
matches = re.match(pattern, self._values['checksum'])
|
||||
if matches:
|
||||
return matches.group('value')
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class KeyParameters(Parameters):
|
||||
api_map = {
|
||||
'sourcePath': 'key_source_path'
|
||||
}
|
||||
|
||||
updatables = ['key_source_path']
|
||||
|
||||
returnables = ['key_filename', 'key_checksum', 'key_source_path']
|
||||
|
||||
api_attributes = ['passphrase', 'sourcePath']
|
||||
|
||||
@property
|
||||
def key_filename(self):
|
||||
fname, fext = os.path.splitext(self.name)
|
||||
if fext == '':
|
||||
return fname + '.key'
|
||||
else:
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def key_checksum(self):
|
||||
if self.key_content is None:
|
||||
return None
|
||||
return self._get_hash(self.key_content)
|
||||
|
||||
@property
|
||||
def key_src(self):
|
||||
if self._values['key_src'] is None:
|
||||
return None
|
||||
|
||||
self._values['__warnings'].append(
|
||||
dict(
|
||||
msg="The key_src param is deprecated",
|
||||
version='2.4'
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
with open(self._values['key_src']) as fh:
|
||||
self.key_content = fh.read()
|
||||
except IOError:
|
||||
raise F5ModuleError(
|
||||
"The specified 'key_src' does not exist"
|
||||
)
|
||||
|
||||
@property
|
||||
def key_source_path(self):
|
||||
result = 'file://' + os.path.join(
|
||||
BaseManager.download_path,
|
||||
self.key_filename
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class CertParameters(Parameters):
|
||||
api_map = {
|
||||
'sourcePath': 'cert_source_path'
|
||||
}
|
||||
|
||||
updatables = ['cert_source_path']
|
||||
|
||||
returnables = ['cert_filename', 'cert_checksum', 'cert_source_path']
|
||||
|
||||
api_attributes = ['sourcePath']
|
||||
|
||||
@property
|
||||
def cert_checksum(self):
|
||||
if self.cert_content is None:
|
||||
return None
|
||||
return self._get_hash(self.cert_content)
|
||||
|
||||
@property
|
||||
def cert_filename(self):
|
||||
fname, fext = os.path.splitext(self.name)
|
||||
if fext == '':
|
||||
return fname + '.crt'
|
||||
else:
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def cert_src(self):
|
||||
if self._values['cert_src'] is None:
|
||||
return None
|
||||
|
||||
self._values['__warnings'].append(
|
||||
dict(
|
||||
msg="The cert_src param is deprecated",
|
||||
version='2.4'
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
with open(self._value['cert_src']) as fh:
|
||||
self.cert_content = fh.read()
|
||||
except IOError:
|
||||
raise F5ModuleError(
|
||||
"The specified 'cert_src' does not exist"
|
||||
)
|
||||
|
||||
@property
|
||||
def cert_source_path(self):
|
||||
result = 'file://' + os.path.join(
|
||||
BaseManager.download_path,
|
||||
self.cert_filename
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class ModuleManager(object):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
def exec_module(self):
|
||||
manager1 = self.get_manager('certificate')
|
||||
manager2 = self.get_manager('key')
|
||||
result = self.execute_managers([manager1, manager2])
|
||||
return result
|
||||
|
||||
def execute_managers(self, managers):
|
||||
results = dict(changed=False)
|
||||
for manager in managers:
|
||||
result = manager.exec_module()
|
||||
for k, v in iteritems(result):
|
||||
if k == 'changed':
|
||||
if v is True:
|
||||
results['changed'] = True
|
||||
else:
|
||||
results[k] = v
|
||||
return results
|
||||
|
||||
def get_manager(self, type):
|
||||
if type == 'certificate':
|
||||
return CertificateManager(self.client)
|
||||
elif type == 'key':
|
||||
return KeyManager(self.client)
|
||||
|
||||
|
||||
class BaseManager(object):
|
||||
download_path = '/var/config/rest/downloads'
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.have = None
|
||||
|
||||
def exec_module(self):
|
||||
changed = False
|
||||
do_key = False
|
||||
do_cert = False
|
||||
chash = None
|
||||
khash = None
|
||||
|
||||
check_mode = self.params['check_mode']
|
||||
name = self.params['name']
|
||||
partition = self.params['partition']
|
||||
cert_content = self.params['cert_content']
|
||||
key_content = self.params['key_content']
|
||||
passphrase = self.params['passphrase']
|
||||
|
||||
# Technically you don't need to provide us with anything in the form
|
||||
# of content for your cert, but that's kind of illogical, so we just
|
||||
# return saying you didn't "do" anything if you left the cert and keys
|
||||
# empty.
|
||||
if not cert_content and not key_content:
|
||||
return False
|
||||
|
||||
if key_content is not None:
|
||||
if 'key_checksum' in current:
|
||||
khash = self.get_hash(key_content)
|
||||
if khash not in current['key_checksum']:
|
||||
do_key = "update"
|
||||
else:
|
||||
do_key = "create"
|
||||
|
||||
if cert_content is not None:
|
||||
if 'cert_checksum' in current:
|
||||
chash = self.get_hash(cert_content)
|
||||
if chash not in current['cert_checksum']:
|
||||
do_cert = "update"
|
||||
else:
|
||||
do_cert = "create"
|
||||
|
||||
if do_cert or do_key:
|
||||
changed = True
|
||||
params = dict()
|
||||
params['cert_name'] = name
|
||||
params['key_name'] = name
|
||||
params['partition'] = partition
|
||||
if khash:
|
||||
params['key_checksum'] = khash
|
||||
if chash:
|
||||
params['cert_checksum'] = chash
|
||||
self.cparams = params
|
||||
|
||||
if check_mode:
|
||||
return changed
|
||||
|
||||
if not do_cert and not do_key:
|
||||
return False
|
||||
|
||||
tx = self.api.tm.transactions.transaction
|
||||
with TransactionContextManager(tx) as api:
|
||||
if do_cert:
|
||||
# Upload the content of a certificate as a StringIO object
|
||||
cstring = StringIO.StringIO(cert_content)
|
||||
filename = "%s.crt" % (name)
|
||||
filepath = os.path.join(self.dlpath, filename)
|
||||
api.shared.file_transfer.uploads.upload_stringio(
|
||||
cstring,
|
||||
filename
|
||||
)
|
||||
|
||||
if do_cert == "update":
|
||||
# Install the certificate
|
||||
params = {
|
||||
'name': name,
|
||||
'partition': partition
|
||||
}
|
||||
cert = api.tm.sys.file.ssl_certs.ssl_cert.load(**params)
|
||||
|
||||
# This works because, while the source path is the same,
|
||||
# calling update causes the file to be re-read
|
||||
cert.update()
|
||||
changed = True
|
||||
elif do_cert == "create":
|
||||
# Install the certificate
|
||||
params = {
|
||||
'sourcePath': "file://" + filepath,
|
||||
'name': name,
|
||||
'partition': partition
|
||||
}
|
||||
api.tm.sys.file.ssl_certs.ssl_cert.create(**params)
|
||||
changed = True
|
||||
|
||||
if do_key:
|
||||
# Upload the content of a certificate key as a StringIO object
|
||||
kstring = StringIO.StringIO(key_content)
|
||||
filename = "%s.key" % (name)
|
||||
filepath = os.path.join(self.dlpath, filename)
|
||||
api.shared.file_transfer.uploads.upload_stringio(
|
||||
kstring,
|
||||
filename
|
||||
)
|
||||
|
||||
if do_key == "update":
|
||||
# Install the key
|
||||
params = {
|
||||
'name': name,
|
||||
'partition': partition
|
||||
}
|
||||
key = api.tm.sys.file.ssl_keys.ssl_key.load(**params)
|
||||
|
||||
params = dict()
|
||||
|
||||
if passphrase:
|
||||
params['passphrase'] = passphrase
|
||||
else:
|
||||
params['passphrase'] = None
|
||||
|
||||
key.update(**params)
|
||||
changed = True
|
||||
elif do_key == "create":
|
||||
# Install the key
|
||||
params = {
|
||||
'sourcePath': "file://" + filepath,
|
||||
'name': name,
|
||||
'partition': partition
|
||||
}
|
||||
if passphrase:
|
||||
params['passphrase'] = self.params['passphrase']
|
||||
else:
|
||||
params['passphrase'] = None
|
||||
|
||||
api.tm.sys.file.ssl_keys.ssl_key.create(**params)
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
def key_exists(self):
|
||||
return self.api.tm.sys.file.ssl_keys.ssl_key.exists(
|
||||
name=self.params['name'],
|
||||
partition=self.params['partition']
|
||||
)
|
||||
|
||||
def cert_exists(self):
|
||||
return self.api.tm.sys.file.ssl_certs.ssl_cert.exists(
|
||||
name=self.params['name'],
|
||||
partition=self.params['partition']
|
||||
)
|
||||
|
||||
def read(self):
|
||||
p = dict()
|
||||
name = self.params['name']
|
||||
partition = self.params['partition']
|
||||
|
||||
if self.key_exists():
|
||||
key = self.api.tm.sys.file.ssl_keys.ssl_key.load(
|
||||
name=name,
|
||||
partition=partition
|
||||
)
|
||||
if hasattr(key, 'checksum'):
|
||||
p['key_checksum'] = str(key.checksum)
|
||||
|
||||
if self.cert_exists():
|
||||
cert = self.api.tm.sys.file.ssl_certs.ssl_cert.load(
|
||||
name=name,
|
||||
partition=partition
|
||||
)
|
||||
if hasattr(cert, 'checksum'):
|
||||
p['cert_checksum'] = str(cert.checksum)
|
||||
|
||||
p['name'] = name
|
||||
return p
|
||||
|
||||
def flush(self):
|
||||
result = dict()
|
||||
state = self.params['state']
|
||||
state = self.want.state
|
||||
|
||||
try:
|
||||
if state == "present":
|
||||
|
@ -417,94 +391,311 @@ class BigIpSslCertificate(object):
|
|||
except iControlUnexpectedHTTPError as e:
|
||||
raise F5ModuleError(str(e))
|
||||
|
||||
result.update(**self.cparams)
|
||||
changes = self.changes.to_return()
|
||||
result.update(**changes)
|
||||
result.update(dict(changed=changed))
|
||||
self._announce_deprecations()
|
||||
return result
|
||||
|
||||
def absent(self):
|
||||
changed = False
|
||||
def _announce_deprecations(self):
|
||||
warnings = []
|
||||
if self.want:
|
||||
warnings += self.want._values.get('__warnings', [])
|
||||
if self.have:
|
||||
warnings += self.have._values.get('__warnings', [])
|
||||
for warning in warnings:
|
||||
self.client.module.deprecate(
|
||||
msg=warning['msg'],
|
||||
version=warning['version']
|
||||
)
|
||||
|
||||
def present(self):
|
||||
if self.exists():
|
||||
changed = self.delete()
|
||||
return self.update()
|
||||
else:
|
||||
return self.create()
|
||||
|
||||
return changed
|
||||
|
||||
def delete(self):
|
||||
changed = False
|
||||
name = self.params['name']
|
||||
partition = self.params['partition']
|
||||
|
||||
check_mode = self.params['check_mode']
|
||||
|
||||
delete_cert = self.cert_exists()
|
||||
delete_key = self.key_exists()
|
||||
|
||||
if not delete_cert and not delete_key:
|
||||
return changed
|
||||
|
||||
if check_mode:
|
||||
params = dict()
|
||||
params['cert_name'] = name
|
||||
params['key_name'] = name
|
||||
params['partition'] = partition
|
||||
self.cparams = params
|
||||
def create(self):
|
||||
self._set_changed_options()
|
||||
if self.client.check_mode:
|
||||
return True
|
||||
self.create_on_device()
|
||||
return True
|
||||
|
||||
tx = self.api.tm.transactions.transaction
|
||||
with TransactionContextManager(tx) as api:
|
||||
if delete_cert:
|
||||
# Delete the certificate
|
||||
c = api.tm.sys.file.ssl_certs.ssl_cert.load(
|
||||
name=self.params['name'],
|
||||
partition=self.params['partition']
|
||||
)
|
||||
c.delete()
|
||||
changed = True
|
||||
def should_update(self):
|
||||
result = self._update_changed_options()
|
||||
if result:
|
||||
return True
|
||||
return False
|
||||
|
||||
if delete_key:
|
||||
# Delete the certificate key
|
||||
k = self.api.tm.sys.file.ssl_keys.ssl_key.load(
|
||||
name=self.params['name'],
|
||||
partition=self.params['partition']
|
||||
)
|
||||
k.delete()
|
||||
changed = True
|
||||
return changed
|
||||
def update(self):
|
||||
self.have = self.read_current_from_device()
|
||||
if not self.should_update():
|
||||
return False
|
||||
if self.client.check_mode:
|
||||
return True
|
||||
self.update_on_device()
|
||||
return True
|
||||
|
||||
def absent(self):
|
||||
if self.exists():
|
||||
return self.remove()
|
||||
return False
|
||||
|
||||
def remove(self):
|
||||
if self.client.check_mode:
|
||||
return True
|
||||
self.remove_from_device()
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = f5_argument_spec()
|
||||
class CertificateManager(BaseManager):
|
||||
def __init__(self, client):
|
||||
super(CertificateManager, self).__init__(client)
|
||||
self.want = CertParameters(self.client.module.params)
|
||||
self.changes = CertParameters()
|
||||
|
||||
meta_args = dict(
|
||||
name=dict(type='str', required=True),
|
||||
cert_content=dict(type='str', default=None),
|
||||
cert_src=dict(type='path', default=None),
|
||||
key_content=dict(type='str', default=None),
|
||||
key_src=dict(type='path', default=None),
|
||||
passphrase=dict(type='str', default=None, no_log=True)
|
||||
)
|
||||
def _set_changed_options(self):
|
||||
changed = {}
|
||||
try:
|
||||
for key in CertParameters.returnables:
|
||||
if getattr(self.want, key) is not None:
|
||||
changed[key] = getattr(self.want, key)
|
||||
if changed:
|
||||
self.changes = CertParameters(changed)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
def _update_changed_options(self):
|
||||
changed = {}
|
||||
try:
|
||||
for key in CertParameters.updatables:
|
||||
if getattr(self.want, key) is not None:
|
||||
attr1 = getattr(self.want, key)
|
||||
attr2 = getattr(self.have, key)
|
||||
if attr1 != attr2:
|
||||
changed[key] = attr1
|
||||
if self.want.cert_checksum != self.have.checksum:
|
||||
changed['cert_checksum'] = self.want.cert_checksum
|
||||
if changed:
|
||||
self.changes = CertParameters(changed)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[
|
||||
def exists(self):
|
||||
result = self.client.api.tm.sys.file.ssl_certs.ssl_cert.exists(
|
||||
name=self.want.cert_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
return result
|
||||
|
||||
def present(self):
|
||||
if self.want.cert_content is None:
|
||||
return False
|
||||
return super(CertificateManager, self).present()
|
||||
|
||||
def should_update(self):
|
||||
result = self._update_changed_options()
|
||||
if result:
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_on_device(self):
|
||||
content = StringIO(self.want.cert_content)
|
||||
self.client.api.shared.file_transfer.uploads.upload_stringio(
|
||||
content, self.want.cert_filename
|
||||
)
|
||||
resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
|
||||
name=self.want.cert_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
resource.update()
|
||||
|
||||
def create_on_device(self):
|
||||
content = StringIO(self.want.cert_content)
|
||||
self.client.api.shared.file_transfer.uploads.upload_stringio(
|
||||
content, self.want.cert_filename
|
||||
)
|
||||
self.client.api.tm.sys.file.ssl_certs.ssl_cert.create(
|
||||
sourcePath=self.want.cert_source_path,
|
||||
name=self.want.cert_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
|
||||
def read_current_from_device(self):
|
||||
resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
|
||||
name=self.want.cert_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
result = resource.attrs
|
||||
return CertParameters(result)
|
||||
|
||||
def remove_from_device(self):
|
||||
resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
|
||||
name=self.want.cert_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
resource.delete()
|
||||
|
||||
def remove(self):
|
||||
result = super(CertificateManager, self).remove()
|
||||
if self.exists() and not self.client.check_mode:
|
||||
raise F5ModuleError("Failed to delete the certificate")
|
||||
return result
|
||||
|
||||
|
||||
class KeyManager(BaseManager):
|
||||
def __init__(self, client):
|
||||
super(KeyManager, self).__init__(client)
|
||||
self.want = KeyParameters(self.client.module.params)
|
||||
self.changes = KeyParameters()
|
||||
|
||||
def _set_changed_options(self):
|
||||
changed = {}
|
||||
try:
|
||||
for key in KeyParameters.returnables:
|
||||
if getattr(self.want, key) is not None:
|
||||
changed[key] = getattr(self.want, key)
|
||||
if changed:
|
||||
self.changes = Parameters(changed)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _update_changed_options(self):
|
||||
changed = {}
|
||||
try:
|
||||
for key in CertParameters.updatables:
|
||||
if getattr(self.want, key) is not None:
|
||||
attr1 = getattr(self.want, key)
|
||||
attr2 = getattr(self.have, key)
|
||||
if attr1 != attr2:
|
||||
changed[key] = attr1
|
||||
if self.want.key_checksum != self.have.checksum:
|
||||
changed['key_checksum'] = self.want.key_checksum
|
||||
if changed:
|
||||
self.changes = CertParameters(changed)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def should_update(self):
|
||||
result = self._update_changed_options()
|
||||
if result:
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_on_device(self):
|
||||
content = StringIO(self.want.key_content)
|
||||
self.client.api.shared.file_transfer.uploads.upload_stringio(
|
||||
content, self.want.key_filename
|
||||
)
|
||||
resource = self.client.api.tm.sys.file.ssl_keys.ssl_key.load(
|
||||
name=self.want.key_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
resource.update()
|
||||
|
||||
def exists(self):
|
||||
result = self.client.api.tm.sys.file.ssl_keys.ssl_key.exists(
|
||||
name=self.want.key_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
return result
|
||||
|
||||
def present(self):
|
||||
if self.want.key_content is None:
|
||||
return False
|
||||
return super(KeyManager, self).present()
|
||||
|
||||
def read_current_from_device(self):
|
||||
resource = self.client.api.tm.sys.file.ssl_keys.ssl_key.load(
|
||||
name=self.want.key_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
result = resource.attrs
|
||||
return KeyParameters(result)
|
||||
|
||||
def create_on_device(self):
|
||||
content = StringIO(self.want.key_content)
|
||||
self.client.api.shared.file_transfer.uploads.upload_stringio(
|
||||
content, self.want.key_filename
|
||||
)
|
||||
self.client.api.tm.sys.file.ssl_keys.ssl_key.create(
|
||||
sourcePath=self.want.key_source_path,
|
||||
name=self.want.key_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
|
||||
def remove_from_device(self):
|
||||
resource = self.client.api.tm.sys.file.ssl_keys.ssl_key.load(
|
||||
name=self.want.key_filename,
|
||||
partition=self.want.partition
|
||||
)
|
||||
resource.delete()
|
||||
|
||||
def remove(self):
|
||||
result = super(KeyManager, self).remove()
|
||||
if self.exists() and not self.client.check_mode:
|
||||
raise F5ModuleError("Failed to delete the key")
|
||||
return result
|
||||
|
||||
|
||||
class ArgumentSpec(object):
|
||||
def __init__(self):
|
||||
self.supports_check_mode = True
|
||||
self.argument_spec = dict(
|
||||
name=dict(
|
||||
required=True
|
||||
),
|
||||
cert_content=dict(),
|
||||
cert_src=dict(
|
||||
type='path',
|
||||
removed_in_version='2.4'
|
||||
),
|
||||
key_content=dict(),
|
||||
key_src=dict(
|
||||
type='path',
|
||||
removed_in_version='2.4'
|
||||
),
|
||||
passphrase=dict(
|
||||
no_log=True
|
||||
),
|
||||
state=dict(
|
||||
required=False,
|
||||
default='present',
|
||||
choices=['absent', 'present']
|
||||
)
|
||||
)
|
||||
self.mutually_exclusive = [
|
||||
['key_content', 'key_src'],
|
||||
['cert_content', 'cert_src']
|
||||
]
|
||||
self.f5_product_name = 'bigip'
|
||||
|
||||
|
||||
def main():
|
||||
if not HAS_F5SDK:
|
||||
raise F5ModuleError("The python f5-sdk module is required")
|
||||
|
||||
spec = ArgumentSpec()
|
||||
|
||||
client = AnsibleF5Client(
|
||||
argument_spec=spec.argument_spec,
|
||||
mutually_exclusive=spec.mutually_exclusive,
|
||||
supports_check_mode=spec.supports_check_mode,
|
||||
f5_product_name=spec.f5_product_name
|
||||
)
|
||||
|
||||
try:
|
||||
obj = BigIpSslCertificate(check_mode=module.check_mode,
|
||||
**module.params)
|
||||
result = obj.flush()
|
||||
module.exit_json(**result)
|
||||
mm = ModuleManager(client)
|
||||
results = mm.exec_module()
|
||||
client.module.exit_json(**results)
|
||||
except F5ModuleError as e:
|
||||
module.fail_json(msg=str(e))
|
||||
client.module.fail_json(msg=str(e))
|
||||
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.f5_utils import *
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
101
test/units/modules/network/f5/fixtures/cert1.crt
Normal file
101
test/units/modules/network/f5/fixtures/cert1.crt
Normal file
|
@ -0,0 +1,101 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=New York, L=New York, O=ACME CA, OU=Coyote, CN=ourca.domain.local
|
||||
Validity
|
||||
Not Before: Jun 30 16:46:09 2016 GMT
|
||||
Not After : Jun 25 16:46:09 2036 GMT
|
||||
Subject: C=US, ST=New York, O=ACME, OU=Coyote, CN=cert1.domain.local
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:d6:0f:bd:26:ef:14:4d:09:f6:db:8b:01:f5:4e:
|
||||
6c:03:b1:35:20:16:b8:1b:7c:e6:b6:8d:97:1b:b0:
|
||||
4f:8a:b6:cb:54:7e:7a:ff:fd:af:02:db:bf:9d:cf:
|
||||
9a:4c:0d:87:93:8b:cc:61:f3:23:a9:6f:8e:d4:82:
|
||||
2c:93:b6:e2:fa:37:ed:8a:d3:23:8f:6d:b5:78:4a:
|
||||
38:ba:93:f9:4a:1c:40:06:33:d7:c0:98:20:d4:16:
|
||||
ac:a4:a5:6b:41:20:4c:3a:55:7e:c7:50:e7:95:07:
|
||||
4e:86:15:86:7a:0f:6c:57:d2:07:1c:97:24:51:5b:
|
||||
4e:f5:52:3a:f8:4f:95:6b:6c:83:1f:34:4e:ee:b0:
|
||||
ae:fe:46:90:38:f1:4d:85:72:8b:46:bc:d1:62:37:
|
||||
65:5a:de:bb:16:51:1e:f5:cb:a0:ef:d6:7b:11:6f:
|
||||
3b:0c:49:17:bc:4d:8c:f5:d9:f0:35:6b:f7:b6:4d:
|
||||
50:eb:47:81:e3:06:f2:bd:ec:67:4f:ab:2b:03:aa:
|
||||
e2:1e:42:22:a9:c9:59:dc:0d:19:fb:c5:02:1d:d7:
|
||||
58:e4:04:53:0a:1d:79:bb:c1:33:f1:cd:b7:10:2e:
|
||||
b4:6e:9b:dc:60:66:05:50:9f:20:66:a1:71:00:51:
|
||||
54:cf:0a:70:f4:7c:45:c6:f0:a7:1c:11:2f:3e:a3:
|
||||
1f:bf
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
2D:FB:27:C7:B4:32:FF:F7:87:DB:2D:A7:76:AE:F0:96:7E:DA:DC:17
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:4F:2A:15:49:E6:CC:05:2F:2B:F4:0E:CC:BA:2E:4C:DF:13:90:F0:78
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
3f:46:1c:3b:58:b4:99:f3:75:00:47:d2:fe:ba:ba:9a:04:46:
|
||||
62:b6:2d:a0:0f:8f:c0:95:2a:58:8b:61:f5:14:90:30:26:37:
|
||||
94:a1:a6:29:20:c9:b5:08:d7:f9:15:cb:9d:9c:19:ed:2f:a4:
|
||||
e6:91:48:85:1a:f7:ab:17:5e:79:23:69:b8:3c:0c:48:ae:c8:
|
||||
ba:90:d0:05:fb:33:7e:86:fd:12:f8:2d:0f:ff:16:15:9a:dc:
|
||||
76:48:7d:65:5b:4e:93:14:e8:be:37:d1:13:f7:a7:b1:cd:ad:
|
||||
ae:4f:e1:72:b9:53:2d:cd:e6:42:76:44:93:21:28:58:c0:44:
|
||||
ab:3c:da:5b:e5:55:ab:04:86:4d:9c:4c:33:f4:4e:13:98:e9:
|
||||
0f:d1:a3:70:2b:1d:11:20:47:26:f6:d8:45:7f:88:ad:f2:c1:
|
||||
81:0f:be:cd:6c:79:80:94:30:eb:8d:cc:f3:7d:a1:3e:6c:6f:
|
||||
fa:8f:f3:1f:2e:76:97:3f:8a:1b:67:3b:e0:f9:b1:3c:6b:dc:
|
||||
64:1b:00:73:e9:89:81:f6:7f:51:f3:51:c8:b9:96:5f:fd:55:
|
||||
f8:77:6f:88:bc:65:b3:e2:30:a4:00:7a:79:68:e0:36:8b:a9:
|
||||
1b:06:9b:20:fe:fe:98:aa:56:58:c8:08:a4:7b:12:59:ff:3d:
|
||||
bd:5e:13:3b:c6:c7:8a:00:5b:cb:27:18:02:ee:cb:38:c2:b7:
|
||||
a9:51:04:ef:31:ca:49:09:48:14:13:eb:91:e2:26:8c:88:5f:
|
||||
1c:78:e1:0d:90:29:d7:c1:fc:c8:89:fd:4d:53:0b:99:58:c2:
|
||||
1a:24:3d:c0:a2:4c:a3:d9:c7:95:c5:bc:72:fa:02:f1:ab:dd:
|
||||
aa:2b:9e:a0:bb:1a:68:2d:09:8c:a2:99:0d:26:ec:9e:30:19:
|
||||
01:5a:41:45:63:b3:c5:db:24:32:4c:fe:7f:f3:ce:e9:4d:00:
|
||||
64:cf:bb:15:34:2d:31:6e:4f:c0:96:40:9b:32:35:65:92:01:
|
||||
29:7e:74:02:50:fd:3b:3b:3a:a3:9f:6a:c0:a5:be:3f:c3:07:
|
||||
d6:8c:2a:c6:f4:0f:32:bd:3b:fc:45:90:d2:46:ee:6f:c3:2f:
|
||||
26:8c:97:0c:e8:da:9a:97:03:0b:86:17:45:a6:62:69:4e:8d:
|
||||
cf:f8:bf:ea:2f:dc:ff:95:14:15:bd:92:2d:8a:08:cf:ce:8a:
|
||||
b0:f6:34:0a:a2:0e:49:31:44:e1:47:fb:37:52:53:59:93:25:
|
||||
40:cc:ac:67:2d:a2:b6:9b:75:fd:13:a5:a7:93:4f:72:05:75:
|
||||
cd:b1:37:f6:3b:69:3b:24:a1:1f:23:f0:cd:bb:ae:18:b3:aa:
|
||||
eb:9f:d7:97:06:ba:fd:44
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIExjCCAq6gAwIBAgIBATANBgkqhkiG9w0BAQsFADBzMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZb3JrMRAwDgYDVQQKDAdB
|
||||
Q01FIENBMQ8wDQYDVQQLDAZDb3lvdGUxGzAZBgNVBAMMEm91cmNhLmRvbWFpbi5s
|
||||
b2NhbDAeFw0xNjA2MzAxNjQ2MDlaFw0zNjA2MjUxNjQ2MDlaMF0xCzAJBgNVBAYT
|
||||
AlVTMREwDwYDVQQIDAhOZXcgWW9yazENMAsGA1UECgwEQUNNRTEPMA0GA1UECwwG
|
||||
Q295b3RlMRswGQYDVQQDDBJjZXJ0MS5kb21haW4ubG9jYWwwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDWD70m7xRNCfbbiwH1TmwDsTUgFrgbfOa2jZcb
|
||||
sE+KtstUfnr//a8C27+dz5pMDYeTi8xh8yOpb47UgiyTtuL6N+2K0yOPbbV4Sji6
|
||||
k/lKHEAGM9fAmCDUFqykpWtBIEw6VX7HUOeVB06GFYZ6D2xX0gcclyRRW071Ujr4
|
||||
T5VrbIMfNE7usK7+RpA48U2FcotGvNFiN2Va3rsWUR71y6Dv1nsRbzsMSRe8TYz1
|
||||
2fA1a/e2TVDrR4HjBvK97GdPqysDquIeQiKpyVncDRn7xQId11jkBFMKHXm7wTPx
|
||||
zbcQLrRum9xgZgVQnyBmoXEAUVTPCnD0fEXG8KccES8+ox+/AgMBAAGjezB5MAkG
|
||||
A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp
|
||||
ZmljYXRlMB0GA1UdDgQWBBQt+yfHtDL/94fbLad2rvCWftrcFzAfBgNVHSMEGDAW
|
||||
gBRPKhVJ5swFLyv0Dsy6LkzfE5DweDANBgkqhkiG9w0BAQsFAAOCAgEAP0YcO1i0
|
||||
mfN1AEfS/rq6mgRGYrYtoA+PwJUqWIth9RSQMCY3lKGmKSDJtQjX+RXLnZwZ7S+k
|
||||
5pFIhRr3qxdeeSNpuDwMSK7IupDQBfszfob9EvgtD/8WFZrcdkh9ZVtOkxTovjfR
|
||||
E/ensc2trk/hcrlTLc3mQnZEkyEoWMBEqzzaW+VVqwSGTZxMM/ROE5jpD9GjcCsd
|
||||
ESBHJvbYRX+IrfLBgQ++zWx5gJQw643M832hPmxv+o/zHy52lz+KG2c74PmxPGvc
|
||||
ZBsAc+mJgfZ/UfNRyLmWX/1V+HdviLxls+IwpAB6eWjgNoupGwabIP7+mKpWWMgI
|
||||
pHsSWf89vV4TO8bHigBbyycYAu7LOMK3qVEE7zHKSQlIFBPrkeImjIhfHHjhDZAp
|
||||
18H8yIn9TVMLmVjCGiQ9wKJMo9nHlcW8cvoC8avdqiueoLsaaC0JjKKZDSbsnjAZ
|
||||
AVpBRWOzxdskMkz+f/PO6U0AZM+7FTQtMW5PwJZAmzI1ZZIBKX50AlD9Ozs6o59q
|
||||
wKW+P8MH1owqxvQPMr07/EWQ0kbub8MvJoyXDOjampcDC4YXRaZiaU6Nz/i/6i/c
|
||||
/5UUFb2SLYoIz86KsPY0CqIOSTFE4Uf7N1JTWZMlQMysZy2itpt1/ROlp5NPcgV1
|
||||
zbE39jtpOyShHyPwzbuuGLOq65/Xlwa6/UQ=
|
||||
-----END CERTIFICATE-----
|
27
test/units/modules/network/f5/fixtures/cert1.key
Normal file
27
test/units/modules/network/f5/fixtures/cert1.key
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA1g+9Ju8UTQn224sB9U5sA7E1IBa4G3zmto2XG7BPirbLVH56
|
||||
//2vAtu/nc+aTA2Hk4vMYfMjqW+O1IIsk7bi+jftitMjj221eEo4upP5ShxABjPX
|
||||
wJgg1BaspKVrQSBMOlV+x1DnlQdOhhWGeg9sV9IHHJckUVtO9VI6+E+Va2yDHzRO
|
||||
7rCu/kaQOPFNhXKLRrzRYjdlWt67FlEe9cug79Z7EW87DEkXvE2M9dnwNWv3tk1Q
|
||||
60eB4wbyvexnT6srA6riHkIiqclZ3A0Z+8UCHddY5ARTCh15u8Ez8c23EC60bpvc
|
||||
YGYFUJ8gZqFxAFFUzwpw9HxFxvCnHBEvPqMfvwIDAQABAoIBAQCjQ7PP+y8vpvbp
|
||||
8bbXoy2ND15mkA1xoazR9WIYEzxHny2rzx//GTyfYH1gXtPfR75tEYYb+vbrJxP4
|
||||
DyTysN2jXH7HkEwh+9oZ2fo0i+Hp3WwTjvzyftUjDfw1Q5lvPbQGFekxGgrXRpBk
|
||||
ggxkEllfDeiwrLJdftfVEhe6BfD/0YibwQeHN7VoC4V8wOanKtDmx74W/1f7WhwQ
|
||||
nKQnCrbYqNJa2nGvWiKU5Suvfb0v7tCnQYlfnCpUfj+wcnxlgmGkcyq1L+qC1qC8
|
||||
PO5i3T3LM5Yg8CSeGhO/q6gw/fUowuBN1cluTqN97oLHiEM5tLdjeVWwa1Vp0liv
|
||||
1WXGT4eBAoGBAPtumMmyVTIorvV6KGNI/Eo6jfE0HOXVdXtm4iToDDuiYwto7/Ge
|
||||
/kV+11Fpu0lV+eYPfZn175Of8FnQPwczQF1OOH/aQ/ViY8j87bZUbCy25mWrfNkh
|
||||
2rRlyI3/OsSfL5SkyWpYB0yhSJZV9mSQJTZolB4GQRNPKtqi7NpB4WxBAoGBANnz
|
||||
VS4JBJO75yeSG5BzPp5VVKm+nu0Betlva8GsHdEic8OM9bGpVozGysAW3Xdxp7q6
|
||||
gLJGyyuzpsxldCc/IdIlF5fz7gkLl4NoYanz9PSEr2XZLh9+2yXGkPFlC3IeHAUB
|
||||
E+2UO9MFpWrmfKoAnYZCR6vJDxtQBpAlTUvJEYv/AoGBAPha62K32327P+7MJl7D
|
||||
9ijgI9rwjebcbbpiCtlHuOWi5lCb6/7v/NvqiYcqeEvdOAXuoTNWAbsBTel5UPis
|
||||
wFQp8pcfouccs9IRPEFQrLWSSIx+0sirrxtoOq1AQe18DAS4rRd1MmiYG1ocOVBm
|
||||
LcvLixsJNHh9R6hFLM3+K0vBAoGANkmJ+gF9Bl9TYGPgQcay3jVa9Tzp0RcBRo+e
|
||||
Q4tfkewG8bp2qF4JlN8fOWF4oHvKz5QM4lsH2EbTUS4kFHKBNhrPGaZEsDQW9UBW
|
||||
s0J0zUMPfUrvViD+7RXcnIQSqcYeLJDsKc02aYWKgmoOuzmUAxEXUQ6vmJoCSH1C
|
||||
F5JpsHkCgYEArwTSzb1+/ThQhK1JN8hJ4jMjQ8E7PzLTMILrdDALn2g1T4VzL7N7
|
||||
UG6oUieMlo/UH6cv6330dwaGVklXZbyDKSDROIafFcOpVfcvDUgJCjp3CaY9A2zG
|
||||
+EPkRpeHKXAIgG+QuOwVOtYWcWltnBf61slTqiY2vKX1+ZGmrMrw1Zw=
|
||||
-----END RSA PRIVATE KEY-----
|
101
test/units/modules/network/f5/fixtures/cert2.crt
Normal file
101
test/units/modules/network/f5/fixtures/cert2.crt
Normal file
|
@ -0,0 +1,101 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 2 (0x2)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=New York, L=New York, O=ACME CA, OU=Coyote, CN=ourca.domain.local
|
||||
Validity
|
||||
Not Before: Jun 30 16:49:00 2016 GMT
|
||||
Not After : Jun 25 16:49:00 2036 GMT
|
||||
Subject: C=US, ST=New York, O=ACME, OU=Coyote, CN=cert2.domain.local
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:c6:9e:84:99:4d:69:98:c2:42:95:ed:43:ca:24:
|
||||
05:64:9d:67:81:1c:ff:56:7b:ad:d1:cb:09:39:28:
|
||||
4f:ac:aa:1b:34:61:3a:b1:e3:57:d4:9e:15:40:77:
|
||||
91:20:2b:e8:7e:d3:91:1e:46:50:6c:2f:4b:00:c2:
|
||||
f2:3a:43:89:d9:81:73:84:5f:02:db:49:ac:3b:9e:
|
||||
fe:c0:77:2e:53:ea:ce:da:ff:49:98:21:1d:31:4d:
|
||||
0f:14:20:30:36:9a:23:b4:28:08:06:59:81:30:03:
|
||||
86:09:0b:5b:e1:72:63:5e:54:ac:90:b1:82:55:b8:
|
||||
12:00:d5:01:26:be:6a:eb:fc:58:5b:8a:7a:fe:46:
|
||||
23:a3:eb:5d:6c:e0:f6:79:00:5d:5b:49:82:42:62:
|
||||
e2:58:e8:65:54:14:be:99:25:8b:b7:df:cf:53:26:
|
||||
f2:7a:fd:b9:f9:f3:d5:af:06:d6:1e:ba:66:4d:41:
|
||||
8c:5d:aa:23:41:7f:f4:27:21:a0:30:09:86:13:c4:
|
||||
57:1b:13:45:63:6b:3b:a3:7f:d1:1a:cd:fd:07:51:
|
||||
0f:1a:e1:d9:25:3e:d2:77:e1:c7:60:db:12:df:ef:
|
||||
71:65:c8:c7:1a:42:94:6f:57:2a:d7:67:30:0f:33:
|
||||
31:ba:90:4d:d1:80:38:08:e7:90:7a:04:0e:8f:b0:
|
||||
2a:73
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
43:71:A9:16:2B:DA:DC:5F:FD:82:87:78:26:48:4E:77:21:47:44:D6
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:4F:2A:15:49:E6:CC:05:2F:2B:F4:0E:CC:BA:2E:4C:DF:13:90:F0:78
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
15:ac:f9:cb:bc:88:c0:d3:74:83:88:cd:19:94:bb:87:7e:fd:
|
||||
4d:25:09:9b:08:84:64:c5:37:c7:99:b3:25:ee:6e:82:46:b0:
|
||||
13:f9:05:ad:4d:4a:b2:e3:29:5d:0d:55:9e:9c:62:1d:95:f2:
|
||||
19:49:5e:d3:5b:58:98:ce:e8:f5:e5:c1:ce:b5:a8:7a:b1:f8:
|
||||
14:fe:25:10:12:5b:41:53:d2:47:ab:20:e5:50:da:b6:ba:00:
|
||||
21:94:6b:dd:0b:24:15:dc:c0:4e:b8:1d:cc:9e:5f:10:5e:46:
|
||||
3f:96:c9:f8:28:bb:13:31:d6:d2:6c:48:41:bb:23:ab:23:64:
|
||||
73:d6:2b:2e:9a:77:d4:08:fb:e0:e8:50:2c:49:7f:98:9e:f6:
|
||||
37:30:2b:7c:97:c6:a7:1e:5b:dc:ce:bb:1e:58:e4:bd:05:4c:
|
||||
ad:07:d6:03:c5:a9:57:a4:26:e2:10:f7:f9:63:1a:2a:6a:9c:
|
||||
52:98:33:bf:ea:70:cd:c0:86:32:80:6e:70:54:87:74:3c:41:
|
||||
53:a1:c6:53:44:c7:74:a6:11:b6:48:66:86:f9:04:ca:ec:5d:
|
||||
4f:ce:7f:64:51:34:52:53:98:a8:70:62:f7:3b:fb:39:11:9a:
|
||||
e1:e2:d3:00:0b:6b:d2:33:3c:44:de:c3:6b:e1:6f:c9:be:d2:
|
||||
2c:8a:f0:b3:d3:4c:12:2f:ad:9d:6b:40:89:23:94:93:6d:12:
|
||||
6c:38:89:fa:fe:ad:02:55:55:8b:c3:86:7f:15:c4:3a:a9:70:
|
||||
e9:06:6c:26:09:28:9f:6e:94:f2:a1:27:5c:89:4c:42:ac:65:
|
||||
90:92:d2:6d:09:7c:d8:a1:bf:5b:25:e4:db:ed:71:41:d7:e2:
|
||||
61:47:89:9e:46:29:9d:f9:f4:94:cf:f5:b3:e8:df:6a:47:34:
|
||||
d1:ed:fc:a4:58:fe:82:e1:6e:e9:05:65:f5:d2:57:9a:d1:42:
|
||||
64:ae:0c:bb:07:14:39:a2:c0:85:e4:25:a5:c4:e6:3f:e6:da:
|
||||
d0:18:4f:e0:01:ba:99:2e:1f:75:35:c3:fa:a3:e7:e1:75:1b:
|
||||
1c:19:93:cc:96:eb:3f:ce:8b:10:40:36:63:f5:66:dc:6d:75:
|
||||
31:ba:db:27:21:b4:15:00:e9:ce:d0:08:e3:b0:1c:e3:29:c9:
|
||||
63:5a:c8:5c:ca:db:ce:51:b7:87:22:c6:ba:42:d7:ab:29:b4:
|
||||
87:fa:27:9a:18:22:90:9f:da:c0:90:c4:49:64:38:38:2e:a2:
|
||||
ea:87:c1:8b:4e:8b:ff:a7:53:45:4f:d8:8b:86:69:ea:87:1d:
|
||||
f6:e6:44:14:1f:69:ee:2c:de:5a:a1:df:a8:57:13:65:4d:5b:
|
||||
ce:6e:f2:15:2a:c5:32:08
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIExjCCAq6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADBzMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZb3JrMRAwDgYDVQQKDAdB
|
||||
Q01FIENBMQ8wDQYDVQQLDAZDb3lvdGUxGzAZBgNVBAMMEm91cmNhLmRvbWFpbi5s
|
||||
b2NhbDAeFw0xNjA2MzAxNjQ5MDBaFw0zNjA2MjUxNjQ5MDBaMF0xCzAJBgNVBAYT
|
||||
AlVTMREwDwYDVQQIDAhOZXcgWW9yazENMAsGA1UECgwEQUNNRTEPMA0GA1UECwwG
|
||||
Q295b3RlMRswGQYDVQQDDBJjZXJ0Mi5kb21haW4ubG9jYWwwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDGnoSZTWmYwkKV7UPKJAVknWeBHP9We63Rywk5
|
||||
KE+sqhs0YTqx41fUnhVAd5EgK+h+05EeRlBsL0sAwvI6Q4nZgXOEXwLbSaw7nv7A
|
||||
dy5T6s7a/0mYIR0xTQ8UIDA2miO0KAgGWYEwA4YJC1vhcmNeVKyQsYJVuBIA1QEm
|
||||
vmrr/Fhbinr+RiOj611s4PZ5AF1bSYJCYuJY6GVUFL6ZJYu3389TJvJ6/bn589Wv
|
||||
BtYeumZNQYxdqiNBf/QnIaAwCYYTxFcbE0Vjazujf9Eazf0HUQ8a4dklPtJ34cdg
|
||||
2xLf73FlyMcaQpRvVyrXZzAPMzG6kE3RgDgI55B6BA6PsCpzAgMBAAGjezB5MAkG
|
||||
A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp
|
||||
ZmljYXRlMB0GA1UdDgQWBBRDcakWK9rcX/2Ch3gmSE53IUdE1jAfBgNVHSMEGDAW
|
||||
gBRPKhVJ5swFLyv0Dsy6LkzfE5DweDANBgkqhkiG9w0BAQsFAAOCAgEAFaz5y7yI
|
||||
wNN0g4jNGZS7h379TSUJmwiEZMU3x5mzJe5ugkawE/kFrU1KsuMpXQ1VnpxiHZXy
|
||||
GUle01tYmM7o9eXBzrWoerH4FP4lEBJbQVPSR6sg5VDatroAIZRr3QskFdzATrgd
|
||||
zJ5fEF5GP5bJ+Ci7EzHW0mxIQbsjqyNkc9YrLpp31Aj74OhQLEl/mJ72NzArfJfG
|
||||
px5b3M67HljkvQVMrQfWA8WpV6Qm4hD3+WMaKmqcUpgzv+pwzcCGMoBucFSHdDxB
|
||||
U6HGU0THdKYRtkhmhvkEyuxdT85/ZFE0UlOYqHBi9zv7ORGa4eLTAAtr0jM8RN7D
|
||||
a+Fvyb7SLIrws9NMEi+tnWtAiSOUk20SbDiJ+v6tAlVVi8OGfxXEOqlw6QZsJgko
|
||||
n26U8qEnXIlMQqxlkJLSbQl82KG/WyXk2+1xQdfiYUeJnkYpnfn0lM/1s+jfakc0
|
||||
0e38pFj+guFu6QVl9dJXmtFCZK4MuwcUOaLAheQlpcTmP+ba0BhP4AG6mS4fdTXD
|
||||
+qPn4XUbHBmTzJbrP86LEEA2Y/Vm3G11MbrbJyG0FQDpztAI47Ac4ynJY1rIXMrb
|
||||
zlG3hyLGukLXqym0h/onmhgikJ/awJDESWQ4OC6i6ofBi06L/6dTRU/Yi4Zp6ocd
|
||||
9uZEFB9p7izeWqHfqFcTZU1bzm7yFSrFMgg=
|
||||
-----END CERTIFICATE-----
|
30
test/units/modules/network/f5/fixtures/cert2.key
Normal file
30
test/units/modules/network/f5/fixtures/cert2.key
Normal file
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,C56C8101A9C8D6B9AD0975807D4793BB
|
||||
|
||||
rhV1cJee3XAlY83zIXytJOlvXFrsHmzyTVoOn26eOwgza8CuE5gQQzLXiT12zK0q
|
||||
YAbYyyyEXJJogVU61s1vuCQRNiezUCwT4SBj7ni4rXqu5BYUxKh0wD0gjE519yNP
|
||||
nqnPUYKdLkFY7I6RJjqCqkk8xnJm64g5zCqN58aR98Mkqr1898+lZ2OHqAsAYBNH
|
||||
dM/SE7B7E4Mr1sAjpsn6L4PJ93WSwmEtH3nZTnPF9qtFuJwjUcHCN/r/s3QXki95
|
||||
eFX+7qW460lBfDeRUKXKqz4gO017AXu1kccrlHhdQoJGf3D+x9zwofG/uFeAH3iN
|
||||
f9IRaiR2IN6SS67QFmOkI9S95tsFb4N8bmmfGV4w8wfxvDzGJuxzIb4gByX5xov4
|
||||
S22pDpkfn5YqxgC5ItSiFYpg01HEi2l79HwZqAn1kowLsuF1JJKAYL5IMS3DlrdH
|
||||
AyA9CN28G6pYEjwFBbFgpOg64UNmrkxRncHxC4FuH7iGZNJL9+HQve/J5nlrnx6M
|
||||
IU2myiZZhgbsl/V45ddXBDSlEdWFLHtEhcG+ICJP3EZAXHR0e9vyrWDk7T5zKhLP
|
||||
ch9PNmIw+5zzpRuPu5NYw7V0ax8UOf2AydyBHeIQWuY52bai+QMDyQauomqpPXRY
|
||||
tpCcW85P9jstY/F6TV32XQu/cHWolziJXI/QzWF5+uvnLMAsb3p5mriCG4DOTWF3
|
||||
KFSytTGnDQUUCLgaYSSKXL5Z52PVYmTjoqX8M6cvqSEdjK84wILQE0JMItQjGSIM
|
||||
y5qHD7Mthf9YOJy1D86qtVumbaOBLw/rGPQS5QlK/m256xZ10LUslYczMpw1orN3
|
||||
3Uv8zHKk790XduHTllR0LwQXMJXG59hgiWAu3V3rsAkVSRpC3MI6IUZ2cfJvZ0Ds
|
||||
FmUhCJ34JQxD4E/sT9uGAk6VIq/fAmM7/gq0oF4oqOFg4Zy1r3rc1Kvdoy1yKUi6
|
||||
JCI5bKCkgIthx4XUKQVtFMkHBDZAHr6i5Lzy4nM6I4S4/qL3JH4Q+739D1rjGVlq
|
||||
OWcaeOzkkbJrE8h+A94UQao4R50LavKgq/o2n56tHG0RhXXyV5MC/X9rbSVipihR
|
||||
rwNKnogdhAjY96IrOzdiHTArg8qZBGvHPoGUl3zjWFqNbHEs4NLSrEl6oEs6F/vC
|
||||
zEZmi8gxqraw4u1GJnpoMuLO45PuhcxcXgJSvTh/OKDaR1u0ggEn7TxfAygm0ahP
|
||||
i6NBgoZ/upTHAWqWht2JjSmQHQW7doVkp/BgNJq13oYF7FEUEg/ZtBTPKPR3CjM0
|
||||
ZKDGvKqWRVRyrw9FSwXn6WlSFfT3vhPMoW2jq1Kq5o/ZyhcquCVE8i+xq6hilcb5
|
||||
sNiV1tPWsZOFHx4T5hBVK+QnC8t7pCj38YpyEoY4/gffMtY85jsrLMlPYd5bmJ6O
|
||||
x1tKiQauK+aX6IMu38YnHjCGnCkw1fF2OMSohbG2QfaKsmfkt8YLRuf2PTtjLtke
|
||||
xGt0Irjac/sEZPc4SEIqnehNfXadiuMV3+4v6ey9vf782r76KH8gInY2gDsQ4X6d
|
||||
1LVNCNAd/AGlitopL4hYomaeTjTzqIy5fMlGmTrpZjokenu/ILXsljZVAX2iyOAs
|
||||
-----END RSA PRIVATE KEY-----
|
101
test/units/modules/network/f5/fixtures/create_insecure_cert1.crt
Normal file
101
test/units/modules/network/f5/fixtures/create_insecure_cert1.crt
Normal file
|
@ -0,0 +1,101 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=New York, L=New York, O=ACME CA, OU=Coyote, CN=ourca.domain.local
|
||||
Validity
|
||||
Not Before: Jun 30 16:46:09 2016 GMT
|
||||
Not After : Jun 25 16:46:09 2036 GMT
|
||||
Subject: C=US, ST=New York, O=ACME, OU=Coyote, CN=cert1.domain.local
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:d6:0f:bd:26:ef:14:4d:09:f6:db:8b:01:f5:4e:
|
||||
6c:03:b1:35:20:16:b8:1b:7c:e6:b6:8d:97:1b:b0:
|
||||
4f:8a:b6:cb:54:7e:7a:ff:fd:af:02:db:bf:9d:cf:
|
||||
9a:4c:0d:87:93:8b:cc:61:f3:23:a9:6f:8e:d4:82:
|
||||
2c:93:b6:e2:fa:37:ed:8a:d3:23:8f:6d:b5:78:4a:
|
||||
38:ba:93:f9:4a:1c:40:06:33:d7:c0:98:20:d4:16:
|
||||
ac:a4:a5:6b:41:20:4c:3a:55:7e:c7:50:e7:95:07:
|
||||
4e:86:15:86:7a:0f:6c:57:d2:07:1c:97:24:51:5b:
|
||||
4e:f5:52:3a:f8:4f:95:6b:6c:83:1f:34:4e:ee:b0:
|
||||
ae:fe:46:90:38:f1:4d:85:72:8b:46:bc:d1:62:37:
|
||||
65:5a:de:bb:16:51:1e:f5:cb:a0:ef:d6:7b:11:6f:
|
||||
3b:0c:49:17:bc:4d:8c:f5:d9:f0:35:6b:f7:b6:4d:
|
||||
50:eb:47:81:e3:06:f2:bd:ec:67:4f:ab:2b:03:aa:
|
||||
e2:1e:42:22:a9:c9:59:dc:0d:19:fb:c5:02:1d:d7:
|
||||
58:e4:04:53:0a:1d:79:bb:c1:33:f1:cd:b7:10:2e:
|
||||
b4:6e:9b:dc:60:66:05:50:9f:20:66:a1:71:00:51:
|
||||
54:cf:0a:70:f4:7c:45:c6:f0:a7:1c:11:2f:3e:a3:
|
||||
1f:bf
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
2D:FB:27:C7:B4:32:FF:F7:87:DB:2D:A7:76:AE:F0:96:7E:DA:DC:17
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:4F:2A:15:49:E6:CC:05:2F:2B:F4:0E:CC:BA:2E:4C:DF:13:90:F0:78
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
3f:46:1c:3b:58:b4:99:f3:75:00:47:d2:fe:ba:ba:9a:04:46:
|
||||
62:b6:2d:a0:0f:8f:c0:95:2a:58:8b:61:f5:14:90:30:26:37:
|
||||
94:a1:a6:29:20:c9:b5:08:d7:f9:15:cb:9d:9c:19:ed:2f:a4:
|
||||
e6:91:48:85:1a:f7:ab:17:5e:79:23:69:b8:3c:0c:48:ae:c8:
|
||||
ba:90:d0:05:fb:33:7e:86:fd:12:f8:2d:0f:ff:16:15:9a:dc:
|
||||
76:48:7d:65:5b:4e:93:14:e8:be:37:d1:13:f7:a7:b1:cd:ad:
|
||||
ae:4f:e1:72:b9:53:2d:cd:e6:42:76:44:93:21:28:58:c0:44:
|
||||
ab:3c:da:5b:e5:55:ab:04:86:4d:9c:4c:33:f4:4e:13:98:e9:
|
||||
0f:d1:a3:70:2b:1d:11:20:47:26:f6:d8:45:7f:88:ad:f2:c1:
|
||||
81:0f:be:cd:6c:79:80:94:30:eb:8d:cc:f3:7d:a1:3e:6c:6f:
|
||||
fa:8f:f3:1f:2e:76:97:3f:8a:1b:67:3b:e0:f9:b1:3c:6b:dc:
|
||||
64:1b:00:73:e9:89:81:f6:7f:51:f3:51:c8:b9:96:5f:fd:55:
|
||||
f8:77:6f:88:bc:65:b3:e2:30:a4:00:7a:79:68:e0:36:8b:a9:
|
||||
1b:06:9b:20:fe:fe:98:aa:56:58:c8:08:a4:7b:12:59:ff:3d:
|
||||
bd:5e:13:3b:c6:c7:8a:00:5b:cb:27:18:02:ee:cb:38:c2:b7:
|
||||
a9:51:04:ef:31:ca:49:09:48:14:13:eb:91:e2:26:8c:88:5f:
|
||||
1c:78:e1:0d:90:29:d7:c1:fc:c8:89:fd:4d:53:0b:99:58:c2:
|
||||
1a:24:3d:c0:a2:4c:a3:d9:c7:95:c5:bc:72:fa:02:f1:ab:dd:
|
||||
aa:2b:9e:a0:bb:1a:68:2d:09:8c:a2:99:0d:26:ec:9e:30:19:
|
||||
01:5a:41:45:63:b3:c5:db:24:32:4c:fe:7f:f3:ce:e9:4d:00:
|
||||
64:cf:bb:15:34:2d:31:6e:4f:c0:96:40:9b:32:35:65:92:01:
|
||||
29:7e:74:02:50:fd:3b:3b:3a:a3:9f:6a:c0:a5:be:3f:c3:07:
|
||||
d6:8c:2a:c6:f4:0f:32:bd:3b:fc:45:90:d2:46:ee:6f:c3:2f:
|
||||
26:8c:97:0c:e8:da:9a:97:03:0b:86:17:45:a6:62:69:4e:8d:
|
||||
cf:f8:bf:ea:2f:dc:ff:95:14:15:bd:92:2d:8a:08:cf:ce:8a:
|
||||
b0:f6:34:0a:a2:0e:49:31:44:e1:47:fb:37:52:53:59:93:25:
|
||||
40:cc:ac:67:2d:a2:b6:9b:75:fd:13:a5:a7:93:4f:72:05:75:
|
||||
cd:b1:37:f6:3b:69:3b:24:a1:1f:23:f0:cd:bb:ae:18:b3:aa:
|
||||
eb:9f:d7:97:06:ba:fd:44
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIExjCCAq6gAwIBAgIBATANBgkqhkiG9w0BAQsFADBzMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZb3JrMRAwDgYDVQQKDAdB
|
||||
Q01FIENBMQ8wDQYDVQQLDAZDb3lvdGUxGzAZBgNVBAMMEm91cmNhLmRvbWFpbi5s
|
||||
b2NhbDAeFw0xNjA2MzAxNjQ2MDlaFw0zNjA2MjUxNjQ2MDlaMF0xCzAJBgNVBAYT
|
||||
AlVTMREwDwYDVQQIDAhOZXcgWW9yazENMAsGA1UECgwEQUNNRTEPMA0GA1UECwwG
|
||||
Q295b3RlMRswGQYDVQQDDBJjZXJ0MS5kb21haW4ubG9jYWwwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDWD70m7xRNCfbbiwH1TmwDsTUgFrgbfOa2jZcb
|
||||
sE+KtstUfnr//a8C27+dz5pMDYeTi8xh8yOpb47UgiyTtuL6N+2K0yOPbbV4Sji6
|
||||
k/lKHEAGM9fAmCDUFqykpWtBIEw6VX7HUOeVB06GFYZ6D2xX0gcclyRRW071Ujr4
|
||||
T5VrbIMfNE7usK7+RpA48U2FcotGvNFiN2Va3rsWUR71y6Dv1nsRbzsMSRe8TYz1
|
||||
2fA1a/e2TVDrR4HjBvK97GdPqysDquIeQiKpyVncDRn7xQId11jkBFMKHXm7wTPx
|
||||
zbcQLrRum9xgZgVQnyBmoXEAUVTPCnD0fEXG8KccES8+ox+/AgMBAAGjezB5MAkG
|
||||
A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp
|
||||
ZmljYXRlMB0GA1UdDgQWBBQt+yfHtDL/94fbLad2rvCWftrcFzAfBgNVHSMEGDAW
|
||||
gBRPKhVJ5swFLyv0Dsy6LkzfE5DweDANBgkqhkiG9w0BAQsFAAOCAgEAP0YcO1i0
|
||||
mfN1AEfS/rq6mgRGYrYtoA+PwJUqWIth9RSQMCY3lKGmKSDJtQjX+RXLnZwZ7S+k
|
||||
5pFIhRr3qxdeeSNpuDwMSK7IupDQBfszfob9EvgtD/8WFZrcdkh9ZVtOkxTovjfR
|
||||
E/ensc2trk/hcrlTLc3mQnZEkyEoWMBEqzzaW+VVqwSGTZxMM/ROE5jpD9GjcCsd
|
||||
ESBHJvbYRX+IrfLBgQ++zWx5gJQw643M832hPmxv+o/zHy52lz+KG2c74PmxPGvc
|
||||
ZBsAc+mJgfZ/UfNRyLmWX/1V+HdviLxls+IwpAB6eWjgNoupGwabIP7+mKpWWMgI
|
||||
pHsSWf89vV4TO8bHigBbyycYAu7LOMK3qVEE7zHKSQlIFBPrkeImjIhfHHjhDZAp
|
||||
18H8yIn9TVMLmVjCGiQ9wKJMo9nHlcW8cvoC8avdqiueoLsaaC0JjKKZDSbsnjAZ
|
||||
AVpBRWOzxdskMkz+f/PO6U0AZM+7FTQtMW5PwJZAmzI1ZZIBKX50AlD9Ozs6o59q
|
||||
wKW+P8MH1owqxvQPMr07/EWQ0kbub8MvJoyXDOjampcDC4YXRaZiaU6Nz/i/6i/c
|
||||
/5UUFb2SLYoIz86KsPY0CqIOSTFE4Uf7N1JTWZMlQMysZy2itpt1/ROlp5NPcgV1
|
||||
zbE39jtpOyShHyPwzbuuGLOq65/Xlwa6/UQ=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA1g+9Ju8UTQn224sB9U5sA7E1IBa4G3zmto2XG7BPirbLVH56
|
||||
//2vAtu/nc+aTA2Hk4vMYfMjqW+O1IIsk7bi+jftitMjj221eEo4upP5ShxABjPX
|
||||
wJgg1BaspKVrQSBMOlV+x1DnlQdOhhWGeg9sV9IHHJckUVtO9VI6+E+Va2yDHzRO
|
||||
7rCu/kaQOPFNhXKLRrzRYjdlWt67FlEe9cug79Z7EW87DEkXvE2M9dnwNWv3tk1Q
|
||||
60eB4wbyvexnT6srA6riHkIiqclZ3A0Z+8UCHddY5ARTCh15u8Ez8c23EC60bpvc
|
||||
YGYFUJ8gZqFxAFFUzwpw9HxFxvCnHBEvPqMfvwIDAQABAoIBAQCjQ7PP+y8vpvbp
|
||||
8bbXoy2ND15mkA1xoazR9WIYEzxHny2rzx//GTyfYH1gXtPfR75tEYYb+vbrJxP4
|
||||
DyTysN2jXH7HkEwh+9oZ2fo0i+Hp3WwTjvzyftUjDfw1Q5lvPbQGFekxGgrXRpBk
|
||||
ggxkEllfDeiwrLJdftfVEhe6BfD/0YibwQeHN7VoC4V8wOanKtDmx74W/1f7WhwQ
|
||||
nKQnCrbYqNJa2nGvWiKU5Suvfb0v7tCnQYlfnCpUfj+wcnxlgmGkcyq1L+qC1qC8
|
||||
PO5i3T3LM5Yg8CSeGhO/q6gw/fUowuBN1cluTqN97oLHiEM5tLdjeVWwa1Vp0liv
|
||||
1WXGT4eBAoGBAPtumMmyVTIorvV6KGNI/Eo6jfE0HOXVdXtm4iToDDuiYwto7/Ge
|
||||
/kV+11Fpu0lV+eYPfZn175Of8FnQPwczQF1OOH/aQ/ViY8j87bZUbCy25mWrfNkh
|
||||
2rRlyI3/OsSfL5SkyWpYB0yhSJZV9mSQJTZolB4GQRNPKtqi7NpB4WxBAoGBANnz
|
||||
VS4JBJO75yeSG5BzPp5VVKm+nu0Betlva8GsHdEic8OM9bGpVozGysAW3Xdxp7q6
|
||||
gLJGyyuzpsxldCc/IdIlF5fz7gkLl4NoYanz9PSEr2XZLh9+2yXGkPFlC3IeHAUB
|
||||
E+2UO9MFpWrmfKoAnYZCR6vJDxtQBpAlTUvJEYv/AoGBAPha62K32327P+7MJl7D
|
||||
9ijgI9rwjebcbbpiCtlHuOWi5lCb6/7v/NvqiYcqeEvdOAXuoTNWAbsBTel5UPis
|
||||
wFQp8pcfouccs9IRPEFQrLWSSIx+0sirrxtoOq1AQe18DAS4rRd1MmiYG1ocOVBm
|
||||
LcvLixsJNHh9R6hFLM3+K0vBAoGANkmJ+gF9Bl9TYGPgQcay3jVa9Tzp0RcBRo+e
|
||||
Q4tfkewG8bp2qF4JlN8fOWF4oHvKz5QM4lsH2EbTUS4kFHKBNhrPGaZEsDQW9UBW
|
||||
s0J0zUMPfUrvViD+7RXcnIQSqcYeLJDsKc02aYWKgmoOuzmUAxEXUQ6vmJoCSH1C
|
||||
F5JpsHkCgYEArwTSzb1+/ThQhK1JN8hJ4jMjQ8E7PzLTMILrdDALn2g1T4VzL7N7
|
||||
UG6oUieMlo/UH6cv6330dwaGVklXZbyDKSDROIafFcOpVfcvDUgJCjp3CaY9A2zG
|
||||
+EPkRpeHKXAIgG+QuOwVOtYWcWltnBf61slTqiY2vKX1+ZGmrMrw1Zw=
|
||||
-----END RSA PRIVATE KEY-----
|
248
test/units/modules/network/f5/test_bigip_ssl_certificate.py
Normal file
248
test/units/modules/network/f5/test_bigip_ssl_certificate.py
Normal file
|
@ -0,0 +1,248 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2017 F5 Networks Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
if sys.version_info < (2, 7):
|
||||
raise SkipTest("F5 Ansible modules require Python >= 2.7")
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.module_utils import basic
|
||||
from ansible.compat.tests.mock import patch, Mock
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.f5_utils import AnsibleF5Client
|
||||
|
||||
try:
|
||||
from library.bigip_ssl_certificate import ArgumentSpec
|
||||
from library.bigip_ssl_certificate import KeyParameters
|
||||
from library.bigip_ssl_certificate import CertParameters
|
||||
from library.bigip_ssl_certificate import CertificateManager
|
||||
from library.bigip_ssl_certificate import KeyManager
|
||||
except ImportError:
|
||||
try:
|
||||
from ansible.modules.network.f5.bigip_ssl_certificate import ArgumentSpec
|
||||
from ansible.modules.network.f5.bigip_ssl_certificate import KeyParameters
|
||||
from ansible.modules.network.f5.bigip_ssl_certificate import CertParameters
|
||||
from ansible.modules.network.f5.bigip_ssl_certificate import CertificateManager
|
||||
from ansible.modules.network.f5.bigip_ssl_certificate import KeyManager
|
||||
except ImportError:
|
||||
raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
|
||||
|
||||
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
|
||||
fixture_data = {}
|
||||
|
||||
|
||||
def set_module_args(args):
|
||||
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
|
||||
basic._ANSIBLE_ARGS = to_bytes(args)
|
||||
|
||||
|
||||
def load_fixture(name):
|
||||
path = os.path.join(fixture_path, name)
|
||||
|
||||
if path in fixture_data:
|
||||
return fixture_data[path]
|
||||
|
||||
with open(path) as f:
|
||||
data = f.read()
|
||||
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
fixture_data[path] = data
|
||||
return data
|
||||
|
||||
|
||||
class TestParameters(unittest.TestCase):
|
||||
def test_module_parameters_key(self):
|
||||
key_content = load_fixture('create_insecure_key1.key')
|
||||
args = dict(
|
||||
key_content=key_content,
|
||||
name="cert1",
|
||||
partition="Common",
|
||||
state="present",
|
||||
password='password',
|
||||
server='localhost',
|
||||
user='admin'
|
||||
)
|
||||
p = KeyParameters(args)
|
||||
assert p.name == 'cert1'
|
||||
assert p.key_filename == 'cert1.key'
|
||||
assert '-----BEGIN RSA PRIVATE KEY-----' in p.key_content
|
||||
assert '-----END RSA PRIVATE KEY-----' in p.key_content
|
||||
assert p.key_checksum == '91bdddcf0077e2bb2a0258aae2ae3117be392e83'
|
||||
assert p.state == 'present'
|
||||
assert p.user == 'admin'
|
||||
assert p.server == 'localhost'
|
||||
assert p.password == 'password'
|
||||
assert p.partition == 'Common'
|
||||
|
||||
def test_module_parameters_cert(self):
|
||||
cert_content = load_fixture('create_insecure_cert1.crt')
|
||||
args = dict(
|
||||
cert_content=cert_content,
|
||||
name="cert1",
|
||||
partition="Common",
|
||||
state="present",
|
||||
password='password',
|
||||
server='localhost',
|
||||
user='admin'
|
||||
)
|
||||
p = CertParameters(args)
|
||||
assert p.name == 'cert1'
|
||||
assert p.cert_filename == 'cert1.crt'
|
||||
assert 'Signature Algorithm' in p.cert_content
|
||||
assert '-----BEGIN CERTIFICATE-----' in p.cert_content
|
||||
assert '-----END CERTIFICATE-----' in p.cert_content
|
||||
assert p.cert_checksum == '1e55aa57ee166a380e756b5aa4a835c5849490fe'
|
||||
assert p.state == 'present'
|
||||
assert p.user == 'admin'
|
||||
assert p.server == 'localhost'
|
||||
assert p.password == 'password'
|
||||
assert p.partition == 'Common'
|
||||
|
||||
|
||||
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
|
||||
return_value=True)
|
||||
class TestCertificateManager(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.spec = ArgumentSpec()
|
||||
|
||||
def test_import_certificate_and_key_no_key_passphrase(self, *args):
|
||||
set_module_args(dict(
|
||||
name='foo',
|
||||
cert_content=load_fixture('cert1.crt'),
|
||||
key_content=load_fixture('cert1.key'),
|
||||
state='present',
|
||||
password='passsword',
|
||||
server='localhost',
|
||||
user='admin'
|
||||
))
|
||||
|
||||
client = AnsibleF5Client(
|
||||
argument_spec=self.spec.argument_spec,
|
||||
supports_check_mode=self.spec.supports_check_mode,
|
||||
f5_product_name=self.spec.f5_product_name
|
||||
)
|
||||
|
||||
# Override methods in the specific type of manager
|
||||
cm = CertificateManager(client)
|
||||
cm.exists = Mock(side_effect=[False, True])
|
||||
cm.create_on_device = Mock(return_value=True)
|
||||
|
||||
results = cm.exec_module()
|
||||
|
||||
assert results['changed'] is True
|
||||
|
||||
def test_update_certificate_new_certificate_and_key_password_protected_key(self, *args):
|
||||
set_module_args(dict(
|
||||
name='foo',
|
||||
cert_content=load_fixture('cert2.crt'),
|
||||
key_content=load_fixture('cert2.key'),
|
||||
state='present',
|
||||
passphrase='keypass',
|
||||
password='passsword',
|
||||
server='localhost',
|
||||
user='admin'
|
||||
))
|
||||
|
||||
client = AnsibleF5Client(
|
||||
argument_spec=self.spec.argument_spec,
|
||||
supports_check_mode=self.spec.supports_check_mode,
|
||||
f5_product_name=self.spec.f5_product_name
|
||||
)
|
||||
|
||||
# Override methods in the specific type of manager
|
||||
cm = CertificateManager(client)
|
||||
cm.exists = Mock(side_effect=[False, True])
|
||||
cm.create_on_device = Mock(return_value=True)
|
||||
|
||||
results = cm.exec_module()
|
||||
|
||||
assert results['changed'] is True
|
||||
|
||||
|
||||
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
|
||||
return_value=True)
|
||||
class TestKeyManager(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.spec = ArgumentSpec()
|
||||
|
||||
def test_import_certificate_and_key_no_key_passphrase(self, *args):
|
||||
set_module_args(dict(
|
||||
name='foo',
|
||||
cert_content=load_fixture('cert1.crt'),
|
||||
key_content=load_fixture('cert1.key'),
|
||||
state='present',
|
||||
password='passsword',
|
||||
server='localhost',
|
||||
user='admin'
|
||||
))
|
||||
|
||||
client = AnsibleF5Client(
|
||||
argument_spec=self.spec.argument_spec,
|
||||
supports_check_mode=self.spec.supports_check_mode,
|
||||
f5_product_name=self.spec.f5_product_name
|
||||
)
|
||||
|
||||
# Override methods in the specific type of manager
|
||||
cm = KeyManager(client)
|
||||
cm.exists = Mock(side_effect=[False, True])
|
||||
cm.create_on_device = Mock(return_value=True)
|
||||
|
||||
results = cm.exec_module()
|
||||
|
||||
assert results['changed'] is True
|
||||
|
||||
def test_update_certificate_new_certificate_and_key_password_protected_key(self, *args):
|
||||
set_module_args(dict(
|
||||
name='foo',
|
||||
cert_content=load_fixture('cert2.crt'),
|
||||
key_content=load_fixture('cert2.key'),
|
||||
state='present',
|
||||
passphrase='keypass',
|
||||
password='passsword',
|
||||
server='localhost',
|
||||
user='admin'
|
||||
))
|
||||
|
||||
client = AnsibleF5Client(
|
||||
argument_spec=self.spec.argument_spec,
|
||||
supports_check_mode=self.spec.supports_check_mode,
|
||||
f5_product_name=self.spec.f5_product_name
|
||||
)
|
||||
|
||||
# Override methods in the specific type of manager
|
||||
cm = KeyManager(client)
|
||||
cm.exists = Mock(side_effect=[False, True])
|
||||
cm.create_on_device = Mock(return_value=True)
|
||||
|
||||
results = cm.exec_module()
|
||||
|
||||
assert results['changed'] is True
|
Loading…
Reference in a new issue