Merge "NSXv3: Add certificate expiration alert"
This commit is contained in:
commit
363ae9446e
@ -31,6 +31,11 @@ NSX_OPENSTACK_IDENTITY = "com.vmware.nsx.openstack"
|
|||||||
_SECRET = None
|
_SECRET = None
|
||||||
|
|
||||||
|
|
||||||
|
def reset_secret():
|
||||||
|
global _SECRET
|
||||||
|
_SECRET = None
|
||||||
|
|
||||||
|
|
||||||
def generate_secret_from_password(password):
|
def generate_secret_from_password(password):
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
m.update(password.encode('ascii'))
|
m.update(password.encode('ascii'))
|
||||||
|
@ -12,20 +12,21 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import os
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron import version as n_version
|
from neutron import version as n_version
|
||||||
from neutron_lib import context as q_context
|
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.common import exceptions as nsx_exc
|
||||||
from vmware_nsx.plugins.nsx_v3 import cert_utils
|
from vmware_nsx.plugins.nsx_v3 import cert_utils
|
||||||
from vmware_nsxlib import v3
|
from vmware_nsxlib import v3
|
||||||
from vmware_nsxlib.v3 import client_cert
|
from vmware_nsxlib.v3 import client_cert
|
||||||
from vmware_nsxlib.v3 import config
|
from vmware_nsxlib.v3 import config
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
NSX_NEUTRON_PLUGIN = 'NSX Neutron plugin'
|
NSX_NEUTRON_PLUGIN = 'NSX Neutron plugin'
|
||||||
OS_NEUTRON_ID_SCOPE = 'os-neutron-id'
|
OS_NEUTRON_ID_SCOPE = 'os-neutron-id'
|
||||||
|
|
||||||
@ -38,20 +39,39 @@ class DbCertProvider(client_cert.ClientCertProvider):
|
|||||||
Since several connections may use same filename simultaneously,
|
Since several connections may use same filename simultaneously,
|
||||||
this class maintains refcount to write/delete the file only once
|
this class maintains refcount to write/delete the file only once
|
||||||
"""
|
"""
|
||||||
|
EXPIRATION_ALERT_DAYS = 30 # days prior to expiration
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
super(DbCertProvider, self).__init__(filename)
|
super(DbCertProvider, self).__init__(filename)
|
||||||
self.refcount = 0
|
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):
|
def __enter__(self):
|
||||||
self.refcount += 1
|
self.refcount += 1
|
||||||
|
|
||||||
if self.refcount > 1:
|
if self.refcount > 1:
|
||||||
return
|
# The file was prepared for another connection
|
||||||
|
# and was not removed yet
|
||||||
|
return self
|
||||||
|
|
||||||
|
try:
|
||||||
context = q_context.get_admin_context()
|
context = q_context.get_admin_context()
|
||||||
db_storage_driver = cert_utils.DbCertificateStorageDriver(context)
|
db_storage_driver = cert_utils.DbCertificateStorageDriver(context)
|
||||||
cert_manager = client_cert.ClientCertificateManager(
|
with client_cert.ClientCertificateManager(
|
||||||
cert_utils.NSX_OPENSTACK_IDENTITY, None, db_storage_driver)
|
cert_utils.NSX_OPENSTACK_IDENTITY,
|
||||||
|
None,
|
||||||
|
db_storage_driver) as cert_manager:
|
||||||
if not cert_manager.exists():
|
if not cert_manager.exists():
|
||||||
msg = _("Unable to load from nsx-db")
|
msg = _("Unable to load from nsx-db")
|
||||||
raise nsx_exc.ClientCertificateException(err_msg=msg)
|
raise nsx_exc.ClientCertificateException(err_msg=msg)
|
||||||
@ -59,17 +79,26 @@ class DbCertProvider(client_cert.ClientCertProvider):
|
|||||||
if not os.path.exists(os.path.dirname(self._filename)):
|
if not os.path.exists(os.path.dirname(self._filename)):
|
||||||
if len(os.path.dirname(self._filename)) > 0:
|
if len(os.path.dirname(self._filename)) > 0:
|
||||||
os.makedirs(os.path.dirname(self._filename))
|
os.makedirs(os.path.dirname(self._filename))
|
||||||
|
|
||||||
cert_manager.export_pem(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
|
||||||
|
|
||||||
LOG.debug("Prepared client certificate file")
|
LOG.debug("Prepared client certificate file")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
def _on_exit(self):
|
||||||
self.refcount -= 1
|
self.refcount -= 1
|
||||||
if self.refcount == 0:
|
if self.refcount == 0 and os.path.isfile(self._filename):
|
||||||
os.remove(self._filename)
|
os.remove(self._filename)
|
||||||
LOG.debug("Deleted client certificate file")
|
LOG.debug("Deleted client certificate file")
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
self._on_exit()
|
||||||
|
|
||||||
def filename(self):
|
def filename(self):
|
||||||
return self._filename
|
return self._filename
|
||||||
|
|
||||||
|
@ -151,9 +151,9 @@ def show_cert(resource, event, trigger, **kwargs):
|
|||||||
cert_data = cert.get_subject()
|
cert_data = cert.get_subject()
|
||||||
cert_data['alg'] = cert.get_signature_alg()
|
cert_data['alg'] = cert.get_signature_alg()
|
||||||
cert_data['key_size'] = cert.get_key_size()
|
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. "
|
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,
|
{'date': expires_on,
|
||||||
'days': expires_in_days})
|
'days': expires_in_days})
|
||||||
|
|
||||||
|
@ -81,12 +81,13 @@ class NsxV3iClientCertProviderTestCase(unittest.TestCase):
|
|||||||
cfg.CONF.set_override('nsx_client_cert_storage',
|
cfg.CONF.set_override('nsx_client_cert_storage',
|
||||||
storage_type, 'nsx_v3')
|
storage_type, 'nsx_v3')
|
||||||
|
|
||||||
if cert_file:
|
|
||||||
cfg.CONF.set_override('nsx_client_cert_file', cert_file, 'nsx_v3')
|
cfg.CONF.set_override('nsx_client_cert_file', cert_file, 'nsx_v3')
|
||||||
if password:
|
|
||||||
cfg.CONF.set_override('nsx_client_cert_pk_password',
|
cfg.CONF.set_override('nsx_client_cert_pk_password',
|
||||||
password, 'nsx_v3')
|
password, 'nsx_v3')
|
||||||
|
|
||||||
|
# pk password secret is cached - reset it for each test
|
||||||
|
cert_utils.reset_secret()
|
||||||
|
|
||||||
self._provider = utils.get_client_cert_provider()
|
self._provider = utils.get_client_cert_provider()
|
||||||
|
|
||||||
def validate_db_provider(self, expected_cert_data):
|
def validate_db_provider(self, expected_cert_data):
|
||||||
@ -126,7 +127,14 @@ class NsxV3iClientCertProviderTestCase(unittest.TestCase):
|
|||||||
self.assertRaises(nsx_exc.ClientCertificateException,
|
self.assertRaises(nsx_exc.ClientCertificateException,
|
||||||
self._provider.__enter__)
|
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"""
|
"""Verify successful certificate load from storage"""
|
||||||
|
|
||||||
self._init_config()
|
self._init_config()
|
||||||
|
Loading…
Reference in New Issue
Block a user