Browse Source

NSXv3: Add certificate expiration alert

A warning will be printed in log if cert expires in less than
30 days.
In addition, fix refcount in cert provider and unit test.

Change-Id: I8899e84c37d56602736b8fb0c1994ad04a5d5b14
changes/79/441979/9
Anna Khmelnitsky 5 years ago
parent
commit
dc78683291
  1. 5
      vmware_nsx/plugins/nsx_v3/cert_utils.py
  2. 65
      vmware_nsx/plugins/nsx_v3/utils.py
  3. 4
      vmware_nsx/shell/admin/plugins/nsxv3/resources/certificates.py
  4. 20
      vmware_nsx/tests/unit/nsx_v3/test_client_cert.py

5
vmware_nsx/plugins/nsx_v3/cert_utils.py

@ -31,6 +31,11 @@ NSX_OPENSTACK_IDENTITY = "com.vmware.nsx.openstack"
_SECRET = None
def reset_secret():
global _SECRET
_SECRET = None
def generate_secret_from_password(password):
m = hashlib.md5()
m.update(password.encode('ascii'))

65
vmware_nsx/plugins/nsx_v3/utils.py

@ -12,20 +12,21 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_config import cfg
from oslo_log import log as logging
from neutron import version as n_version
from neutron_lib import context as q_context
from vmware_nsx._i18n import _LE, _LW
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.plugins.nsx_v3 import cert_utils
from vmware_nsxlib import v3
from vmware_nsxlib.v3 import client_cert
from vmware_nsxlib.v3 import config
import os
NSX_NEUTRON_PLUGIN = 'NSX Neutron plugin'
OS_NEUTRON_ID_SCOPE = 'os-neutron-id'
@ -38,38 +39,66 @@ class DbCertProvider(client_cert.ClientCertProvider):
Since several connections may use same filename simultaneously,
this class maintains refcount to write/delete the file only once
"""
EXPIRATION_ALERT_DAYS = 30 # days prior to expiration
def __init__(self, filename):
super(DbCertProvider, self).__init__(filename)
self.refcount = 0
def _check_expiration(self, expires_in_days):
if expires_in_days > self.EXPIRATION_ALERT_DAYS:
return
if expires_in_days < 0:
LOG.error(_LE("Client certificate has expired %d days ago."),
expires_in_days * -1)
else:
LOG.warning(_LW("Client certificate expires in %d days. "
"Once expired, service will become unavailable."),
expires_in_days)
def __enter__(self):
self.refcount += 1
if self.refcount > 1:
return
context = q_context.get_admin_context()
db_storage_driver = cert_utils.DbCertificateStorageDriver(context)
cert_manager = client_cert.ClientCertificateManager(
cert_utils.NSX_OPENSTACK_IDENTITY, None, db_storage_driver)
if not cert_manager.exists():
msg = _("Unable to load from nsx-db")
raise nsx_exc.ClientCertificateException(err_msg=msg)
if not os.path.exists(os.path.dirname(self._filename)):
if len(os.path.dirname(self._filename)) > 0:
os.makedirs(os.path.dirname(self._filename))
# The file was prepared for another connection
# and was not removed yet
return self
try:
context = q_context.get_admin_context()
db_storage_driver = cert_utils.DbCertificateStorageDriver(context)
with client_cert.ClientCertificateManager(
cert_utils.NSX_OPENSTACK_IDENTITY,
None,
db_storage_driver) as cert_manager:
if not cert_manager.exists():
msg = _("Unable to load from nsx-db")
raise nsx_exc.ClientCertificateException(err_msg=msg)
if not os.path.exists(os.path.dirname(self._filename)):
if len(os.path.dirname(self._filename)) > 0:
os.makedirs(os.path.dirname(self._filename))
cert_manager.export_pem(self._filename)
expires_in_days = cert_manager.expires_in_days()
self._check_expiration(expires_in_days)
except Exception as e:
self._on_exit()
raise e
cert_manager.export_pem(self._filename)
LOG.debug("Prepared client certificate file")
return self
def __exit__(self, type, value, traceback):
def _on_exit(self):
self.refcount -= 1
if self.refcount == 0:
if self.refcount == 0 and os.path.isfile(self._filename):
os.remove(self._filename)
LOG.debug("Deleted client certificate file")
def __exit__(self, type, value, traceback):
self._on_exit()
def filename(self):
return self._filename

4
vmware_nsx/shell/admin/plugins/nsxv3/resources/certificates.py

@ -149,9 +149,9 @@ def show_cert(resource, event, trigger, **kwargs):
cert_data = cert.get_subject()
cert_data['alg'] = cert.get_signature_alg()
cert_data['key_size'] = cert.get_key_size()
if expires_in_days > 0:
if expires_in_days >= 0:
LOG.info(_LI("Client certificate is valid. "
"Expires on %(date)s (in %(days)d days)."),
"Expires on %(date)s UTC (in %(days)d days)."),
{'date': expires_on,
'days': expires_in_days})

20
vmware_nsx/tests/unit/nsx_v3/test_client_cert.py

@ -81,11 +81,12 @@ class NsxV3iClientCertProviderTestCase(unittest.TestCase):
cfg.CONF.set_override('nsx_client_cert_storage',
storage_type, 'nsx_v3')
if cert_file:
cfg.CONF.set_override('nsx_client_cert_file', cert_file, 'nsx_v3')
if password:
cfg.CONF.set_override('nsx_client_cert_pk_password',
password, 'nsx_v3')
cfg.CONF.set_override('nsx_client_cert_file', cert_file, 'nsx_v3')
cfg.CONF.set_override('nsx_client_cert_pk_password',
password, 'nsx_v3')
# pk password secret is cached - reset it for each test
cert_utils.reset_secret()
self._provider = utils.get_client_cert_provider()
@ -126,7 +127,14 @@ class NsxV3iClientCertProviderTestCase(unittest.TestCase):
self.assertRaises(nsx_exc.ClientCertificateException,
self._provider.__enter__)
def x_test_db_provider_with_cert(self):
# now verify return to normal after failure
mock.patch(
"vmware_nsx.db.db.get_certificate",
return_value=(self.CERT, self.PKEY)).start()
self.validate_db_provider(self.CERT + self.PKEY)
def test_db_provider_with_cert(self):
"""Verify successful certificate load from storage"""
self._init_config()

Loading…
Cancel
Save