Skip to content

Commit

Permalink
Merge "Fix to support multiple cert managers with LBaaS" into R2.22.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Zuul authored and opencontrail-ci-admin committed Feb 26, 2016
2 parents b2230b5 + 554bb1c commit ed22be4
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 78 deletions.
4 changes: 2 additions & 2 deletions src/vnsw/agent/init/agent_param.cc
Expand Up @@ -543,8 +543,8 @@ void AgentParam::ParseServiceInstance() {
"SERVICE-INSTANCE.netns_timeout");
GetValueFromTree<string>(si_lb_ssl_cert_path_,
"SERVICE-INSTANCE.lb_ssl_cert_path");
GetValueFromTree<string>(si_lb_keystone_auth_conf_path_,
"SERVICE-INSTANCE.lb_keystone_auth_conf_path");
GetValueFromTree<string>(si_lb_custom_attr_conf_path_,
"SERVICE-INSTANCE.lb_custom_attr_conf_path");
}

void AgentParam::ParseNexthopServer() {
Expand Down
6 changes: 3 additions & 3 deletions src/vnsw/agent/init/agent_param.h
Expand Up @@ -121,8 +121,8 @@ class AgentParam {
std::string si_lb_ssl_cert_path() const {
return si_lb_ssl_cert_path_;
}
std::string si_lb_keystone_auth_conf_path() const {
return si_lb_keystone_auth_conf_path_;
std::string si_lb_custom_attr_conf_path() const {
return si_lb_custom_attr_conf_path_;
}

std::string nexthop_server_endpoint() const {
Expand Down Expand Up @@ -405,7 +405,7 @@ class AgentParam {
int si_netns_workers_;
int si_netns_timeout_;
std::string si_lb_ssl_cert_path_;
std::string si_lb_keystone_auth_conf_path_;
std::string si_lb_custom_attr_conf_path_;
VmwareMode vmware_mode_;
// List of IP addresses on the compute node.
AddressList compute_node_address_list_;
Expand Down
19 changes: 17 additions & 2 deletions src/vnsw/agent/oper/instance_manager.cc
Expand Up @@ -99,12 +99,13 @@ class InstanceManager::NamespaceStaleCleaner {
//If Loadbalncer, delete the config files as well
if (prop.service_type == ServiceInstance::LoadBalancer) {
std::stringstream pathgen;
std::stringstream conf_path, json_path, sock_path;
std::stringstream conf_path, json_path, sock_path, crt_path;

pathgen << manager_->loadbalancer_config_path_ << prop.pool_id;
conf_path << pathgen.str() << ".haproxy.conf";
json_path << pathgen.str() << ".conf.json";
sock_path << pathgen.str() << ".haproxy.sock";
crt_path << pathgen.str() << ".crtbundle.pem";

boost::system::error_code error;
if (fs::exists(conf_path.str())) {
Expand All @@ -130,6 +131,14 @@ class InstanceManager::NamespaceStaleCleaner {
<< error.message());
}
}

if (fs::exists(crt_path.str())) {
fs::remove_all(crt_path.str(), error);
if (error) {
LOG(ERROR, "Stale loadbalancer certificate file delete error"
<< error.message());
}
}
}
}
}
Expand Down Expand Up @@ -764,10 +773,11 @@ void InstanceManager::LoadbalancerObserver(
lb_config_->GenerateConfig(pathgen.str(), loadbalancer->uuid(),
*loadbalancer->properties());
} else {
std::stringstream conf_path, json_path, sock_path;
std::stringstream conf_path, json_path, sock_path, crt_path;
conf_path << pathgen.str() << ".haproxy.conf";
json_path << pathgen.str() << ".conf.json";
sock_path << pathgen.str() << ".haproxy.sock";
crt_path << pathgen.str() << ".crtbundle.pem";

boost::filesystem::path conf_file(conf_path.str());
if (boost::filesystem::exists(conf_file, error)) {
Expand All @@ -783,6 +793,11 @@ void InstanceManager::LoadbalancerObserver(
if (boost::filesystem::exists(json_file, error)) {
boost::filesystem::remove_all(json_path.str(), error);
}

boost::filesystem::path crt_file(crt_path.str());
if (boost::filesystem::exists(crt_file, error)) {
boost::filesystem::remove_all(crt_path.str(), error);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/vnsw/agent/oper/netns_instance_adapter.cc
Expand Up @@ -38,9 +38,9 @@ InstanceTask* NetNSInstanceAdapter::CreateStartTask(const ServiceInstance::Prope
cmd_str << " --cfg-file " << loadbalancer_config_path_ <<
props.pool_id << ".conf.json";
cmd_str << " --pool-id " << props.pool_id;
if (!agent_->params()->si_lb_keystone_auth_conf_path().empty()) {
cmd_str << " --keystone-auth-cfg-file " <<
agent_->params()->si_lb_keystone_auth_conf_path();
if (!agent_->params()->si_lb_custom_attr_conf_path().empty()) {
cmd_str << " --custom-attr-cfg-file " <<
agent_->params()->si_lb_custom_attr_conf_path();
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/vnsw/opencontrail-vrouter-netns/SConscript
Expand Up @@ -33,7 +33,8 @@ sources = [
'opencontrail_vrouter_netns/linux/utils.py',
'opencontrail_vrouter_netns/tests/__init__.py',
'opencontrail_vrouter_netns/tests/test_vrouter_netns.py',
'opencontrail_vrouter_netns/tests/test_vrouter_docker.py'
'opencontrail_vrouter_netns/tests/test_vrouter_docker.py',
'opencontrail_vrouter_netns/tests/test_lbaas_custom_attributes.py'
]

cd_cmd = 'cd ' + Dir('.').path + ' && '
Expand Down
@@ -1,16 +1,48 @@
import json
import keystone_auth
import sys
import logging
import os
import requests
import abc
import six

class Barbican_Cert_Manager(object):
@six.add_metaclass(abc.ABCMeta)
class Cert_Manager(object):
"""Class to download certs from specific
drivers mentioned in the conf_file"""
def __init__(self):
pass

def _request(self, url, headers=None, body=None, request_type=None):
try:
if request_type == 'PUT':
encoded_body = json.dumps(body)
return requests.put(url, headers=headers, data=encoded_body)
elif request_type == 'POST':
encoded_body = json.dumps(body)
return requests.post(url, headers=headers, data=encoded_body)
else:
return requests.get(url, headers=headers)

except Exception as e:
logging.error("Failed sending request to keystone")
return None

@abc.abstractmethod
def _validate_tls_secret(self, tls_container_ref):
pass

@abc.abstractmethod
def _populate_tls_pem(self, tls_container_ref):
pass

class Barbican_Cert_Manager(Cert_Manager):
"""Class to download certs from barbican and
populate the pem file as required by HAProxy
"""
def __init__(self, keystone_auth_conf_file):
self.identity = keystone_auth.Identity(keystone_auth_conf_file)
if not self.identity:
raise Exception()
def __init__(self, identity=None):
super(Barbican_Cert_Manager, self).__init__()
self.identity = identity

def _get_barbican_entity(self, barbican_ep, auth_token,
entity_ref, metadata=True):
Expand All @@ -25,7 +57,7 @@ def _get_barbican_entity(self, barbican_ep, auth_token,
"X-Auth-Token": "%s" % auth_token
}
url = entity_ref
resp = keystone_auth._request(url, headers, 'GET')
resp = self._request(url, headers, 'GET')
if resp.status_code in range(200, 299):
if metadata:
return json.loads(resp.text)
Expand Down Expand Up @@ -102,3 +134,32 @@ def _populate_tls_pem(self, tls_container_ref):
except Exception as e:
logging.error("%s while populating SSL Pem file" % str(e))
return None


class Generic_Cert_Manager(Cert_Manager):
"""Class to download certs from Generic Cert Manager and
populate the pem file as required by HAProxy
"""
def __init__(self, identity=None):
super(Generic_Cert_Manager, self).__init__()

def _validate_tls_secret(self, tls_container_ref):
if tls_container_ref is None:
return False

# Check if the file exists
if not os.path.isfile(tls_container_ref):
return False

# Check if file is readable
if not os.access(tls_container_ref, os.R_OK):
return False

return True

def _populate_tls_pem(self, tls_container_ref):
secret_text = ''
with open(tls_container_ref) as tls_container:
secret_text = tls_container.read()

return secret_text
Expand Up @@ -2,19 +2,20 @@
import os
import logging

def validate_custom_attributes(config, section, keystone_auth_conf_file=None):
def validate_custom_attributes(config, section, custom_attr_conf_file=None):
return {}

try:
from haproxy_validator import validate_custom_attributes as validator
from haproxy_validator import custom_attributes_dict
from haproxy_cert import Barbican_Cert_Manager
except ImportError:
validator = validate_custom_attributes
custom_attributes_dict = {}

# Setup logger
logging.basicConfig(filename='/var/log/contrail/haproxy_parse.log', level=logging.WARNING)
FORMAT="[%(filename)s:%(lineno)s] %(message)s"
logging.basicConfig(filename='/var/log/contrail/haproxy_parse.log',
level=logging.WARNING, format=FORMAT)

# Setup global definitions
PROTO_TCP = 'TCP'
Expand Down Expand Up @@ -44,7 +45,7 @@ def validate_custom_attributes(config, section, keystone_auth_conf_file=None):

HTTPS_PORT = 443

def build_config(pool_id, conf_file, keystone_auth_conf_file):
def build_config(pool_id, conf_file, custom_attr_conf_file=None):
with open(conf_file) as data_file:
config = json.load(data_file)
conf_dir = os.path.dirname(conf_file)
Expand All @@ -53,14 +54,15 @@ def build_config(pool_id, conf_file, keystone_auth_conf_file):
sock_path = conf_dir + '/' + pool_id + '.haproxy.sock'
conf = _set_global_config(config, sock_path) + '\n\n'
conf += _set_defaults(config) + '\n\n'
conf += _set_frontend(config, conf_dir, keystone_auth_conf_file) + '\n\n'
conf += _set_frontend(config, conf_dir, custom_attr_conf_file) + '\n\n'
conf += _set_backend(config) + '\n'
filename = conf_dir + '/' + pool_id + '.haproxy.conf'
conf_file = open(filename, 'w')
conf_file.write(conf)
return filename

def _construct_config_block(lb_config, conf, custom_attr_section, custom_attributes):
def _construct_config_block(lb_config, conf, custom_attr_section,
custom_attributes):
for key, value in custom_attributes.iteritems():
cmd = custom_attributes_dict[custom_attr_section][key]['cmd']
conf.append(cmd % value)
Expand Down Expand Up @@ -92,7 +94,8 @@ def _set_global_config(config, sock_path):
]
conf.append('stats socket %s mode 0666 level user' % sock_path)

return _construct_config_block(config, conf, "global", global_custom_attributes)
return _construct_config_block(config, conf, "global",
global_custom_attributes)


def _set_defaults(config):
Expand All @@ -114,16 +117,17 @@ def _set_defaults(config):
'timeout server %d' % server_timeout,
]

return _construct_config_block(config, conf, "default", default_custom_attributes)
return _construct_config_block(config, conf, "default",
default_custom_attributes)

def _set_frontend(config, conf_dir, keystone_auth_conf_file):
def _set_frontend(config, conf_dir, custom_attr_conf_file=None):
port = config['vip']['port']
vip_custom_attributes = validator(config, 'vip', keystone_auth_conf_file)
vip_custom_attributes = validator(config, 'vip', custom_attr_conf_file)
ssl = ''

if 'tls_container' in vip_custom_attributes:
data = vip_custom_attributes.pop('tls_container', None)
crt_file = _populate_pem_file(data, conf_dir)
crt_file = _populate_pem_file(data, conf_dir, config['pool']['id'])
else:
crt_file = config['ssl-crt']

Expand Down Expand Up @@ -220,8 +224,8 @@ def _get_codes(codes):
response.add(code)
return response

def _populate_pem_file(data, conf_dir):
crt_filename = conf_dir + '/crtbundle.pem'
def _populate_pem_file(data, conf_dir, pool_id):
crt_filename = conf_dir + '/' + pool_id + '.crtbundle.pem'
with open(crt_filename, 'w+') as outfile:
outfile.write(data)

Expand Down
Expand Up @@ -17,10 +17,10 @@ def stop_haproxy(conf_file, daemon_mode=False):
pass

def start_update_haproxy(conf_file, netns, daemon_mode=False,
keystone_auth_conf_file=None):
custom_attr_conf_file=None):
pool_id = conf_file.split('.')[0].split('/')[-1]
haproxy_cfg_file = haproxy_config.build_config(pool_id, conf_file, \
keystone_auth_conf_file)
custom_attr_conf_file)
try:
if daemon_mode:
_start_haproxy_daemon(netns, haproxy_cfg_file)
Expand Down
@@ -1,7 +1,10 @@
import logging
import inspect
from haproxy_cert import Barbican_Cert_Manager
import ConfigParser
import keystone_auth
import os
import sys
import haproxy_cert

class CustomAttr(object):
"""This type handles non-flat data-types like
Expand All @@ -18,9 +21,48 @@ def post_validation(self, conf_list):
return

class CustomAttrTlsContainer(CustomAttr):
def __init__(self, keystone_auth_conf_file, key, value):
def __init__(self, key, value, custom_attr_conf_file=None):
super(CustomAttrTlsContainer, self).__init__(key, value)
self.cert_manager = Barbican_Cert_Manager(keystone_auth_conf_file)
if not custom_attr_conf_file:
logging.error("Missing custom attr conf file name in vrouter-agent.conf")
raise Exception()

if not self._read_config(custom_attr_conf_file):
logging.error("Error reading %s" % custom_attr_conf_file)
raise Exception()

cert_dict = self._parse_args(self._config, 'CERT')
if not cert_dict or not 'cert_manager' in cert_dict:
logging.error("Missing CERT section")
raise Exception()

if cert_dict['cert_manager'] == 'Barbican_Cert_Manager':
# Make sure keystone credentials are present
auth_dict = self._parse_args(self._config, 'KEYSTONE')
if not auth_dict:
logging.error("Missing KEYSTONE section")
raise Exception()
identity = keystone_auth.Identity(auth_dict)
else:
identity = None

self.cert_manager = getattr(haproxy_cert,
cert_dict['cert_manager'])(identity=identity)

def _read_config(self, conf_file):
config = ConfigParser.ConfigParser()
if not len(config.read(conf_file)):
return False
else:
self._config = config
return True

def _parse_args(self, config, section):
try:
return dict(config.items(section))
except Exception as e:
logging.error(str(e))
return None

def validate(self):
if self._key != 'tls_container':
Expand Down Expand Up @@ -118,7 +160,7 @@ def post_validation(self):
'pool': {},
}

def validate_custom_attributes(config, section, keystone_auth_conf_file=None):
def validate_custom_attributes(config, section, custom_attr_conf_file=None):
section_dict = {}
if 'custom-attributes' in config and section in custom_attributes_dict:
custom_attributes = config['custom-attributes']
Expand Down Expand Up @@ -152,8 +194,8 @@ def validate_custom_attributes(config, section, keystone_auth_conf_file=None):
logging.info("Skipping key: %s, value: %s due to" \
"validation failure" % (key, value))
elif inspect.isclass(type_attr):
new_custom_attr = type_attr(keystone_auth_conf_file, \
key, value)
new_custom_attr = type_attr(key, value,
custom_attr_conf_file)
if new_custom_attr.validate():
value = new_custom_attr.post_validation()
section_dict.update({key:value})
Expand Down

0 comments on commit ed22be4

Please sign in to comment.