Skip to content

Commit

Permalink
Merge "Closes-Bug: #1494892. Backporting SSL changes from 2.22-dev to…
Browse files Browse the repository at this point in the history
… 2.20 svc monitor changes to have keystone certificate based ssl handshake Change-Id: I37c48f1faf5d9599489e967c5c40b8445c71b1db" into R2.20
  • Loading branch information
Zuul authored and opencontrail-ci-admin committed Oct 16, 2015
2 parents 1d8b08a + 8884426 commit 2ac270e
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 46 deletions.
148 changes: 126 additions & 22 deletions src/api-lib/vnc_api.py
Expand Up @@ -12,6 +12,7 @@
import time
import platform
import __main__ as main
import ssl

import gen.resource_common
from gen.resource_xsd import *
Expand All @@ -20,6 +21,7 @@

from cfgm_common import rest, utils
from cfgm_common.exceptions import *
from cfgm_common import ssl_adapter

from pprint import pformat

Expand Down Expand Up @@ -84,6 +86,15 @@ class VncApi(VncApiClientGen):
_DEFAULT_AUTHN_TENANT = VncApiClientGen._tenant_name
_DEFAULT_DOMAIN_ID = "default"

# Keystone and and vnc-api SSL support
# contrail-api will remain to be on http
# with LB (haproxy/F5/nginx..etc) configured for
# ssl termination on port 8082(default contrail-api port)
_DEFAULT_API_SERVER_CONNECT="http"
_DEFAULT_API_SERVER_SSL_CONNECT="https"
_DEFAULT_KS_CERT_BUNDLE="/tmp/keystonecertbundle.pem"
_DEFAULT_API_CERT_BUNDLE="/tmp/apiservercertbundle.pem"

# Connection to api-server through Quantum
_DEFAULT_WEB_PORT = 8082
_DEFAULT_BASE_URL = "/"
Expand All @@ -92,12 +103,16 @@ class VncApi(VncApiClientGen):
# a POST /list-bulk-collection is issued
POST_FOR_LIST_THRESHOLD = 25

# Number of pools and number of pool per conn to api-server
_DEFAULT_MAX_POOLS = 100
_DEFAULT_MAX_CONNS_PER_POOL = 100

def __init__(self, username=None, password=None, tenant_name=None,
api_server_host='127.0.0.1', api_server_port='8082',
api_server_url=None, conf_file=None, user_info=None,
auth_token=None, auth_host=None, auth_port=None,
auth_protocol = None, auth_url=None, auth_type=None,
wait_for_connect=False):
wait_for_connect=False, api_server_use_ssl=False):
# TODO allow for username/password to be present in creds file

super(VncApi, self).__init__(self._obj_serializer_diff)
Expand All @@ -109,6 +124,14 @@ def __init__(self, username=None, password=None, tenant_name=None,
except Exception as e:
logger = logging.getLogger(__name__)
logger.warn("Exception: %s", str(e))

self._api_connect_protocol = VncApi._DEFAULT_API_SERVER_CONNECT
# API server SSL Support
use_ssl = api_server_use_ssl
if isinstance(api_server_use_ssl, basestring):
use_ssl = (api_server_use_ssl.lower() == 'true')
if use_ssl:
self._api_connect_protocol = VncApi._DEFAULT_API_SERVER_SSL_CONNECT

# keystone
self._authn_type = auth_type or \
Expand Down Expand Up @@ -137,6 +160,43 @@ def __init__(self, username=None, password=None, tenant_name=None,
self._tenant_name = tenant_name or \
_read_cfg(cfg_parser, 'auth', 'AUTHN_TENANT',
self._DEFAULT_AUTHN_TENANT)

#contrail-api SSL support
try:
self._apiinsecure = cfg_parser.getboolean('global','insecure')
except (AttributeError,
ConfigParser.NoOptionError,
ConfigParser.NoSectionError):
self._apiinsecure = False
apicertfile=_read_cfg(cfg_parser,'global','certfile','')
apikeyfile=_read_cfg(cfg_parser,'global','keyfile','')
apicafile=_read_cfg(cfg_parser,'global','cafile','')

self._use_api_certs=False
if apicertfile and apikeyfile \
and apicafile and api_server_use_ssl:
certs=[apicertfile, apikeyfile, apicafile]
self._apicertbundle=utils.getCertKeyCaBundle(VncApi._DEFAULT_API_CERT_BUNDLE,certs)
self._use_api_certs=True

# keystone SSL support
try:
self._ksinsecure = cfg_parser.getboolean('auth', 'insecure')
except (AttributeError,
ConfigParser.NoOptionError,
ConfigParser.NoSectionError):
self._ksinsecure = False
kscertfile=_read_cfg(cfg_parser,'auth','certfile','')
kskeyfile=_read_cfg(cfg_parser,'auth','keyfile','')
kscafile=_read_cfg(cfg_parser,'auth','cafile','')

self._use_ks_certs=False
if kscertfile and kskeyfile and kscafile \
and self._authn_protocol == 'https':
certs=[kscertfile, kskeyfile, kscafile]
self._kscertbundle=utils.getCertKeyCaBundle(VncApi._DEFAULT_KS_CERT_BUNDLE,certs)
self._use_ks_certs=True

if 'v2' in self._authn_url:
self._authn_body = \
'{"auth":{"passwordCredentials":{' + \
Expand Down Expand Up @@ -177,6 +237,13 @@ def __init__(self, username=None, password=None, tenant_name=None,
else:
self._web_port = api_server_port

self._max_pools = int(_read_cfg(
cfg_parser, 'global', 'MAX_POOLS',
self._DEFAULT_MAX_POOLS))
self._max_conns_per_pool = int(_read_cfg(
cfg_parser, 'global', 'MAX_CONNS_PER_POOL',
self._DEFAULT_MAX_CONNS_PER_POOL))

# Where client's view of world begins
if not api_server_url:
self._base_url = _read_cfg(cfg_parser, 'global', 'BASE_URL',
Expand Down Expand Up @@ -253,10 +320,12 @@ def _obj_serializer_all(self, obj):
def _create_api_server_session(self):
self._api_server_session = requests.Session()

adapter = requests.adapters.HTTPAdapter(pool_connections=100,
pool_maxsize=100)
adapter = requests.adapters.HTTPAdapter(pool_connections=self._max_conns_per_pool,
pool_maxsize=self._max_pools)
ssladapter = ssl_adapter.SSLAdapter(ssl.PROTOCOL_SSLv23)
ssladapter.init_poolmanager(connections=self._max_conns_per_pool,maxsize=self._max_pools)
self._api_server_session.mount("http://", adapter)
self._api_server_session.mount("https://", adapter)
self._api_server_session.mount("https://", ssladapter)
#end _create_api_server_session

# Authenticate with configured service
Expand All @@ -267,8 +336,15 @@ def _authenticate(self, response=None, headers=None):
self._authn_url)
new_headers = headers or {}
try:
response = requests.post(url, data=self._authn_body,
headers=self._DEFAULT_AUTHN_HEADERS)
if self._ksinsecure:
response = requests.post(url, data=self._authn_body,
headers=self._DEFAULT_AUTHN_HEADERS, verify=False)
elif not self._ksinsecure and self._use_ks_certs:
response = requests.post(url, data=self._authn_body,
headers=self._DEFAULT_AUTHN_HEADERS, verify=self._kscertbundle)
else:
response = requests.post(url, data=self._authn_body,
headers=self._DEFAULT_AUTHN_HEADERS)
except Exception as e:
raise RuntimeError('Unable to connect to keystone for authentication. Verify keystone server details')

Expand All @@ -286,10 +362,17 @@ def _authenticate(self, response=None, headers=None):
#end _authenticate

def _http_get(self, uri, headers=None, query_params=None):
url = "http://%s:%s%s" \
% (self._web_host, self._web_port, uri)
response = self._api_server_session.get(url, headers=headers,
params=query_params)
url = "%s://%s:%s%s" \
% (self._api_connect_protocol,self._web_host, self._web_port, uri)
if self._apiinsecure:
response = self._api_server_session.get(url, headers=headers,
params=query_params,verify=False)
elif not self._apiinsecure and self._use_api_certs:
response = self._api_server_session.get(url, headers=headers,
params=query_params,verify=self._apicertbundle)
else:
response = self._api_server_session.get(url, headers=headers,
params=query_params)
#print 'Sending Request URL: ' + pformat(url)
#print ' Headers: ' + pformat(headers)
#print ' QParams: ' + pformat(query_params)
Expand All @@ -300,26 +383,47 @@ def _http_get(self, uri, headers=None, query_params=None):
#end _http_get

def _http_post(self, uri, body, headers):
url = "http://%s:%s%s" \
% (self._web_host, self._web_port, uri)
response = self._api_server_session.post(url, data=body,
headers=headers)
url = "%s://%s:%s%s" \
% (self._api_connect_protocol, self._web_host, self._web_port, uri)
if self._apiinsecure:
response = self._api_server_session.post(url, data=body,
headers=headers, verify=False)
elif not self._apiinsecure and self._use_api_certs:
response = self._api_server_session.post(url, data=body,
headers=headers, verify=self._apicertbundle)
else:
response = self._api_server_session.post(url, data=body,
headers=headers)
return (response.status_code, response.text)
#end _http_post

def _http_delete(self, uri, body, headers):
url = "http://%s:%s%s" \
% (self._web_host, self._web_port, uri)
response = self._api_server_session.delete(url, data=body,
headers=headers)
url = "%s://%s:%s%s" \
% (self._api_connect_protocol, self._web_host, self._web_port, uri)
if self._apiinsecure:
response = self._api_server_session.delete(url, data=body,
headers=headers, verify=False)
elif not self._apiinsecure and self._use_api_certs:
response = self._api_server_session.delete(url, data=body,
headers=headers, verify=self._apicertbundle)
else:
response = self._api_server_session.delete(url, data=body,
headers=headers)
return (response.status_code, response.text)
#end _http_delete

def _http_put(self, uri, body, headers):
url = "http://%s:%s%s" \
% (self._web_host, self._web_port, uri)
response = self._api_server_session.put(url, data=body,
headers=headers)
url = "%s://%s:%s%s" \
% (self._api_connect_protocol, self._web_host, self._web_port, uri)
if self._apiinsecure:
response = self._api_server_session.put(url, data=body,
headers=headers, verify=False)
elif not self._apiinsecure and self._use_api_certs:
response = self._api_server_session.put(url, data=body,
headers=headers, verify=self._apicertbundle)
else:
response = self._api_server_session.put(url, data=body,
headers=headers)
return (response.status_code, response.text)
#end _http_delete

Expand Down
12 changes: 12 additions & 0 deletions src/config/api-server/vnc_auth_keystone.py
Expand Up @@ -22,6 +22,10 @@

from pysandesh.gen_py.sandesh.ttypes import SandeshLevel
from vnc_bottle import get_bottle_server
from cfgm_common import utils as cfgmutils

#keystone SSL cert bundle
_DEFAULT_KS_CERT_BUNDLE="/tmp/keystonecertbundle.pem"

# Open port for access to API server for trouble shooting

Expand Down Expand Up @@ -136,6 +140,11 @@ def __call__(self, env, start_response):
class AuthServiceKeystone(object):

def __init__(self, server_mgr, args):
_kscertbundle=''
if args.certfile and args.keyfile and args.cafile \
and args.auth_protocol == 'https':
certs=[args.certfile, args.keyfile, args.cafile]
_kscertbundle=cfgmutils.getCertKeyCaBundle(_DEFAULT_KS_CERT_BUNDLE,certs)
self._conf_info = {
'auth_host': args.auth_host,
'auth_port': args.auth_port,
Expand All @@ -145,7 +154,10 @@ def __init__(self, server_mgr, args):
'admin_tenant_name': args.admin_tenant_name,
'admin_port': args.admin_port,
'max_requests': args.max_requests,
'insecure':args.insecure,
}
if _kscertbundle:
self._conf_info['cafile'] = _kscertbundle
self._server_mgr = server_mgr
self._auth_method = args.auth
self._multi_tenancy = args.multi_tenancy
Expand Down
1 change: 1 addition & 0 deletions src/config/common/SConscript
Expand Up @@ -36,6 +36,7 @@ local_sources = [
'vnc_kombu.py',
'vnc_db.py',
'dependency_tracker.py',
'ssl_adapter.py',
]
local_sources_rules = []
for file in local_sources:
Expand Down
24 changes: 24 additions & 0 deletions src/config/common/ssl_adapter.py
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# @author: Sanju Abraham, Juniper Networks, OpenContrail
from requests.adapters import HTTPAdapter
try:
from urllib3.poolmanager import PoolManager
except ImportError:
# This is required for redhat7 as it does not have
# separate urllib3 package installed
from requests.packages.urllib3.poolmanager import PoolManager
import ssl

class SSLAdapter(HTTPAdapter):
'''An HTTPS Transport Adapter that can be configured with SSL/TLS version.'''
def __init__(self, ssl_version=None, **kwargs):
self.ssl_version = ssl_version

super(SSLAdapter, self).__init__(**kwargs)

def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(num_pools=connections,
maxsize=maxsize,
block=block,
ssl_version=self.ssl_version)
9 changes: 9 additions & 0 deletions src/config/common/utils.py
Expand Up @@ -108,3 +108,12 @@ def CamelCase(input):
name += w.capitalize()
return name
#end CamelCase

def getCertKeyCaBundle(bundle, certs):
with open(bundle, 'w') as ofile:
for cert in certs:
with open(cert) as ifile:
for line in ifile:
ofile.write(line)
return bundle
#end CreateCertKeyCaBundle
6 changes: 5 additions & 1 deletion src/config/device-manager/device_manager/device_manager.py
Expand Up @@ -148,7 +148,7 @@ def __init__(self, args=None):
self._vnc_lib = VncApi(
args.admin_user, args.admin_password,
args.admin_tenant_name, args.api_server_ip,
args.api_server_port)
args.api_server_port, api_server_use_ssl=args.api_server_use_ssl)
connected = True
self.connection_state_update(ConnectionStatus.UP)
except requests.exceptions.ConnectionError as e:
Expand Down Expand Up @@ -365,6 +365,7 @@ def parse_args(args_str):
--cassandra_server_list 10.1.2.3:9160
--api_server_ip 10.1.2.3
--api_server_port 8082
--api_server_use_ssl False
--zk_server_ip 10.1.2.3
--zk_server_port 2181
--collectors 127.0.0.1:8086
Expand Down Expand Up @@ -399,6 +400,7 @@ def parse_args(args_str):
'cassandra_server_list': '127.0.0.1:9160',
'api_server_ip': '127.0.0.1',
'api_server_port': '8082',
'api_server_use_ssl': False,
'zk_server_ip': '127.0.0.1',
'zk_server_port': '2181',
'collectors': None,
Expand Down Expand Up @@ -462,6 +464,8 @@ def parse_args(args_str):
help="IP address of API server")
parser.add_argument("--api_server_port",
help="Port of API server")
parser.add_argument("--api_server_use_ssl",
help="Use SSL to connect with API server")
parser.add_argument("--zk_server_ip",
help="IP address:port of zookeeper server")
parser.add_argument("--collectors",
Expand Down
7 changes: 6 additions & 1 deletion src/config/schema-transformer/to_bgp.py
Expand Up @@ -3786,6 +3786,7 @@ def parse_args(args_str):
--cassandra_server_list 10.1.2.3:9160
--api_server_ip 10.1.2.3
--api_server_port 8082
--api_server_use_ssl False
--zk_server_ip 10.1.2.3
--zk_server_port 2181
--collectors 127.0.0.1:8086
Expand Down Expand Up @@ -3819,6 +3820,7 @@ def parse_args(args_str):
'cassandra_server_list': '127.0.0.1:9160',
'api_server_ip': '127.0.0.1',
'api_server_port': '8082',
'api_server_use_ssl': False,
'zk_server_ip': '127.0.0.1',
'zk_server_port': '2181',
'collectors': None,
Expand Down Expand Up @@ -3892,6 +3894,8 @@ def parse_args(args_str):
help="IP address of API server")
parser.add_argument("--api_server_port",
help="Port of API server")
parser.add_argument("--api_server_use_ssl",
help="Use SSL to connect with API server")
parser.add_argument("--zk_server_ip",
help="IP address:port of zookeeper server")
parser.add_argument("--collectors",
Expand Down Expand Up @@ -3957,7 +3961,8 @@ def connection_state_update(status, message=None):
try:
_vnc_lib = VncApi(
args.admin_user, args.admin_password, args.admin_tenant_name,
args.api_server_ip, args.api_server_port)
args.api_server_ip, args.api_server_port,
api_server_use_ssl=args.api_server_use_ssl)
connected = True
connection_state_update(ConnectionStatus.UP)
except requests.exceptions.ConnectionError as e:
Expand Down

0 comments on commit 2ac270e

Please sign in to comment.