Skip to content

Commit

Permalink
Merge pull request #1565 from zhaoqin-github/client-ca
Browse files Browse the repository at this point in the history
Support to create listener with client ca certificate
  • Loading branch information
zhaoqin-github authored Dec 22, 2020
2 parents 7f59c5e + b84f6bc commit 1562117
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 23 deletions.
26 changes: 22 additions & 4 deletions f5_openstack_agent/lbaasv2/drivers/bigip/listener_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,35 @@ def add_ssl_profile(self, tls, vip, bigip):
if "default_tls_container_id" in tls:
container_ref = tls["default_tls_container_id"]
self._create_ssl_profile(
container_ref, bigip, vip, True)
container_ref, bigip, vip, True,
client_auth=tls.get("mutual_authentication_up", False),
ca_container_id=tls.get("ca_container_id", None))

if "sni_containers" in tls and tls["sni_containers"]:
for container in tls["sni_containers"]:
container_ref = container["tls_container_id"]
self._create_ssl_profile(container_ref, bigip, vip, False)
self._create_ssl_profile(
container_ref, bigip, vip, False,
client_auth=tls.get("mutual_authentication_up", False),
ca_container_id=tls.get("ca_container_id", None))

def _create_ssl_profile(
self, container_ref, bigip, vip, sni_default=False):
def _create_ssl_profile(self, container_ref, bigip, vip,
sni_default=False, **kwargs):
cert = self.cert_manager.get_certificate(container_ref)
intermediates = self.cert_manager.get_intermediates(container_ref)
key = self.cert_manager.get_private_key(container_ref)
key_passphrase = self.cert_manager.get_private_key_passphrase(
container_ref)

client_auth = kwargs.get("client_auth", False)
c_ca_cref = kwargs.get("ca_container_id", None)
c_ca_cert = None
c_ca_file = None
if c_ca_cref:
c_ca_cert = self.cert_manager.get_certificate(c_ca_cref)
i = c_ca_cref.rindex("/") + 1
c_ca_file = self.service_adapter.prefix + c_ca_cref[i:] + ".crt"

chain = None
if intermediates:
chain = '\n'.join(list(intermediates))
Expand All @@ -208,6 +222,9 @@ def _create_ssl_profile(
bigip, name, cert, key, key_passphrase=key_passphrase,
sni_default=sni_default, intermediates=chain,
parent_profile=self.parent_ssl_profile,
client_auth=client_auth,
client_ca_cert=c_ca_cert,
ca_cert_filename=c_ca_file,
profile_name=profile_name)
except HTTPError as err:
if err.response.status_code != 409:
Expand All @@ -218,6 +235,7 @@ def _create_ssl_profile(
del cert
del chain
del key
del c_ca_cert

# add ssl profile to virtual server
if 'profiles' not in vip:
Expand Down
9 changes: 9 additions & 0 deletions f5_openstack_agent/lbaasv2/drivers/bigip/resource_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,15 @@ def _update_payload(self, old_listener, listener, service, **kwargs):
payload['tls'] = "tls change"
LOG.debug("tls changes happen")

# If client authentication setting changes,
# also need to update client ssl profile.
old_mode = old_listener.get('mutual_authentication_up', False)
new_mode = listener.get('mutual_authentication_up', False)
old_ca = old_listener.get('ca_container_id', "")
new_ca = listener.get('ca_container_id', "")
if old_mode != new_mode or old_ca != new_ca:
payload['tls'] = "tls change"

return super(ListenerManager, self)._update_payload(
old_listener, listener, service,
payload=payload, create_payload=create_payload
Expand Down
17 changes: 9 additions & 8 deletions f5_openstack_agent/lbaasv2/drivers/bigip/service_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,14 +741,15 @@ def get_tls(self, service):
tls = {}
listener = service['listener']
if listener['protocol'] == 'TERMINATED_HTTPS':
if 'default_tls_container_id' in listener and \
listener['default_tls_container_id']:
tls['default_tls_container_id'] = \
listener['default_tls_container_id']

if 'sni_containers' in listener and listener['sni_containers']:
tls['sni_containers'] = listener['sni_containers']

for tls_key in [
'default_tls_container_id',
'sni_containers',
# China mobile client auth parameters
'mutual_authentication_up',
'ca_container_id'
]:
if tls_key in listener and listener[tls_key]:
tls[tls_key] = listener[tls_key]
return tls

def get_name(self, uuid):
Expand Down
47 changes: 38 additions & 9 deletions f5_openstack_agent/lbaasv2/drivers/bigip/ssl_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,19 @@ def create_client_ssl_profile(bigip, name, cert, key, **kwargs):
if not profile_name:
profile_name = name

peerCertMode = "ignore"
client_auth = kwargs.get("client_auth", False)
if client_auth:
peerCertMode = "require"

client_ca_cert = kwargs.get("client_ca_cert", None)
ca_cert_filename = kwargs.get("ca_cert_filename", None)

uploader = bigip.shared.file_transfer.uploads
cert_registrar = bigip.tm.sys.crypto.certs
key_registrar = bigip.tm.sys.crypto.keys
ssl_client_profile = bigip.tm.ltm.profile.client_ssls.client_ssl

# No need to create if it exists
if ssl_client_profile.exists(name=profile_name, partition='Common'):
return

# Check that parent profile exists; use default if not.
if parent_profile and not ssl_client_profile.exists(
name=parent_profile, partition='Common'):
Expand Down Expand Up @@ -86,18 +90,43 @@ def create_client_ssl_profile(bigip, name, cert, key, **kwargs):
'/var/config/rest/downloads/', keyfilename)
key_registrar.exec_cmd('install', **param_set)

if client_ca_cert:
uploader.upload_bytes(client_ca_cert, ca_cert_filename)
# import certificate
param_set = {}
param_set['name'] = ca_cert_filename
param_set['from-local-file'] = os.path.join(
'/var/config/rest/downloads/', ca_cert_filename)
cert_registrar.exec_cmd('install', **param_set)
ca_cert_filename = '/Common/' + ca_cert_filename

# create ssl-client profile from cert/key pair
chain = [{'name': name,
'cert': '/Common/' + certfilename,
'chain': chain_path,
'key': '/Common/' + keyfilename,
'passphrase': key_passphrase}]

ssl_client_profile.create(name=profile_name,
partition='Common',
certKeyChain=chain,
sniDefault=sni_default,
defaultsFrom=parent_profile)
if ssl_client_profile.exists(name=profile_name,
partition='Common'):
profile = ssl_client_profile.load(name=profile_name,
partition='Common')
# BIG-IP v14 or higher version support to modify client
# authentication settings individually.
# See https://cdn.f5.com/product/bugtracker/ID674106.html
profile.modify(certKeyChain=chain,
sniDefault=sni_default,
peerCertMode=peerCertMode,
caFile=ca_cert_filename,
defaultsFrom=parent_profile)
else:
ssl_client_profile.create(name=profile_name,
partition='Common',
certKeyChain=chain,
sniDefault=sni_default,
peerCertMode=peerCertMode,
caFile=ca_cert_filename,
defaultsFrom=parent_profile)
except Exception as err:
LOG.error("Error creating SSL profile: %s" % err.message)
raise SSLProfileError(err.message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def add_ssl_profile_one_tls_no_sni(target, service_with_listener):
assert target._create_ssl_profile.call_count == 1
target._create_ssl_profile.assert_called_once_with(
'12345', bigip, dict(name='name', partition='partition'),
True)
True, ca_container_id=None, client_auth=False)

def add_ssl_profile_no_tls_one_sni(target, service_with_listener):
svc['listener']['protocol'] = 'HTTP'
Expand All @@ -435,7 +435,7 @@ def add_ssl_profile_no_tls_one_sni(target, service_with_listener):
assert target._create_ssl_profile.call_count == 1
target._create_ssl_profile.assert_called_once_with(
'12345', bigip, dict(name='name', partition='partition'),
False)
False, ca_container_id=None, client_auth=False)

self.creation_mode_listener(svc, svc['listeners'][0])
add_ssl_profile_no_tls_or_sni(target, svc)
Expand Down
18 changes: 18 additions & 0 deletions f5_openstack_agent/lbaasv2/drivers/bigip/test/test_ssl_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def test_create_client_ssl_profile_parent_none(self):
'passphrase': None}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand Down Expand Up @@ -85,6 +87,8 @@ def test_create_client_ssl_profile_parent_not_exist(self):
'passphrase': None}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand All @@ -108,6 +112,8 @@ def test_create_client_ssl_profile_parent(self):
'passphrase': None}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom='parentprofile',
)

Expand Down Expand Up @@ -143,6 +149,8 @@ def test_create_client_ssl_intermediates_none(self):
'passphrase': "password"}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand All @@ -163,6 +171,8 @@ def test_create_client_ssl_intermediates(self):
'passphrase': None}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand All @@ -187,6 +197,8 @@ def test_create_client_ssl_intermediates_parent_profile(self):
'passphrase': None}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand All @@ -208,6 +220,8 @@ def test_create_client_ssl_passphrase_none(self):
'passphrase': None}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand All @@ -229,6 +243,8 @@ def test_create_client_ssl_passphrase(self):
'passphrase': "password"}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

Expand All @@ -254,5 +270,7 @@ def test_create_client_ssl_inter_passphrase_profile(self):
'passphrase': 'password'}
],
sniDefault=False,
peerCertMode='ignore',
caFile=None,
defaultsFrom=None,
)

0 comments on commit 1562117

Please sign in to comment.