Skip to content

Commit

Permalink
Closes-Bug: #1494892 - Functionality for secure (HTTPS) connection to…
Browse files Browse the repository at this point in the history
… keystone and api-server using vnc-api

Change-Id: I0e35de70be7d463b076e0e07b5b51474c23a3356
  • Loading branch information
sanju-a committed Sep 20, 2015
1 parent 73f71dc commit 969280e
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 42 deletions.
131 changes: 111 additions & 20 deletions src/api-lib/vnc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import platform
import functools
import __main__ as main
import ssl

import gen.resource_common
import gen.vnc_api_client_gen
Expand All @@ -22,6 +23,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 @@ -88,6 +90,15 @@ class VncApi(object):
_DEFAULT_AUTHN_TENANT = 'default-tenant'
_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 @@ -101,7 +112,7 @@ def __init__(self, username=None, password=None, tenant_name=None,
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

self._obj_serializer = self._obj_serializer_diff
Expand All @@ -123,6 +134,11 @@ def __init__(self, username=None, password=None, tenant_name=None,
logger = logging.getLogger(__name__)
logger.warn("Exception: %s", str(e))

self._api_connect_protocol = VncApi._DEFAULT_API_SERVER_CONNECT
# API server SSL Support
if api_server_use_ssl:
self._api_connect_protocol = VncApi._DEFAULT_API_SERVER_SSL_CONNECT

# keystone
self._authn_type = auth_type or \
_read_cfg(cfg_parser, 'auth', 'AUTHN_TYPE',
Expand Down Expand Up @@ -150,6 +166,44 @@ 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 @@ -384,8 +438,10 @@ def _create_api_server_session(self):

adapter = requests.adapters.HTTPAdapter(pool_connections=100,
pool_maxsize=100)
ssladapter = ssl_adapter.SSLAdapter(ssl.PROTOCOL_SSLv23)
ssladapter.init_poolmanager(connections=100,maxsize=100)
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 @@ -396,8 +452,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 @@ -415,10 +478,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 @@ -429,26 +499,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
13 changes: 12 additions & 1 deletion src/config/api-server/vnc_auth_keystone.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@

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

# Open port for access to API server for trouble shooting
#keystone SSL cert bundle
_DEFAULT_KS_CERT_BUNDLE="/tmp/keystonecertbundle.pem"

# Open port for access to API server for trouble shooting
class LocalAuth(object):

def __init__(self, app, conf_info):
Expand Down Expand Up @@ -136,6 +139,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 +153,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
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ local_sources = [
'vnc_db.py',
'dependency_tracker.py',
'vnc_api_stats.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
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 10 additions & 1 deletion src/config/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,13 @@ def str_to_class(class_name, module_name):

def obj_type_to_vnc_class(obj_type, module_name):
return str_to_class(CamelCase(obj_type), module_name)
# end obj_type_to_vnc_class
# end obj_type_to_vnc_class

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
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,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 @@ -332,6 +332,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 @@ -366,6 +367,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 @@ -430,6 +432,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
6 changes: 5 additions & 1 deletion src/config/schema-transformer/to_bgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,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 @@ -525,6 +526,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 @@ -610,6 +612,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 @@ -687,7 +691,7 @@ 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
6 changes: 5 additions & 1 deletion src/config/svc-monitor/svc_monitor/svc_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,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 @@ -752,6 +753,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 @@ -842,6 +844,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("--collectors",
help="List of VNC collectors in ip:port format",
nargs="+")
Expand Down Expand Up @@ -910,7 +914,7 @@ def run_svc_monitor(args=None):
try:
vnc_api = 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
monitor.logger.api_conn_status_update(ConnectionStatus.UP)
except requests.exceptions.ConnectionError as e:
Expand Down

0 comments on commit 969280e

Please sign in to comment.