Add support for TLS-enabled etcd
With this change CA certificate is saved to /opt/ccp/etc/tls/ca.pem, if that file does not exist. Should be useful to avoid mounting CA certificate to each job, pod, etc. Change-Id: I574d64082e77f49024f49aa7b30c4f2f6cc044ac Depends-On: Ib4b3ea4da7c1f641b9ab0223226348de5eac94df
This commit is contained in:
parent
fbbb667354
commit
d82aebcd82
|
@ -24,6 +24,7 @@ import six
|
|||
VARIABLES = {}
|
||||
GLOBALS_PATH = '/etc/ccp/globals/globals.json'
|
||||
META_FILE = "/etc/ccp/meta/meta.json"
|
||||
CACERT = "/opt/ccp/etc/tls/ca.pem"
|
||||
WORKFLOW_PATH_TEMPLATE = '/etc/ccp/role/%s.json'
|
||||
FILES_DIR = '/etc/ccp/files'
|
||||
EXPORTS_DIR = '/etc/ccp/exports'
|
||||
|
@ -291,24 +292,39 @@ def create_files(files):
|
|||
|
||||
@retry
|
||||
def get_etcd_client():
|
||||
if VARIABLES["security"]["tls"]["enabled"]:
|
||||
LOG.debug("TLS is enabled for etcd, using encrypted connectivity")
|
||||
scheme = "https"
|
||||
ca_cert = CACERT
|
||||
else:
|
||||
scheme = "http"
|
||||
ca_cert = None
|
||||
|
||||
etcd_machines = []
|
||||
# if it's etcd container use local address because container is not
|
||||
# accessible via service due failed readiness check
|
||||
if VARIABLES["role_name"] in ["etcd", "etcd-leader-elector",
|
||||
"etcd-watcher"]:
|
||||
if VARIABLES["security"]["tls"]["enabled"]:
|
||||
# If it's etcd container, connectivity goes over IP address, thus
|
||||
# TLS connection will fail. Need to reuse non-TLS
|
||||
# https://github.com/coreos/etcd/issues/4311
|
||||
scheme = "http"
|
||||
ca_cert = None
|
||||
etcd_address = '127.0.0.1'
|
||||
else:
|
||||
etcd_address = VARIABLES["network_topology"]["private"]["address"]
|
||||
etcd_machines.append(
|
||||
(VARIABLES["network_topology"]["private"]["address"],
|
||||
VARIABLES["etcd"]["client_port"]['cont']))
|
||||
(etcd_address, VARIABLES["etcd"]["client_port"]['cont']))
|
||||
else:
|
||||
etcd_machines.append(
|
||||
(address('etcd'), VARIABLES["etcd"]["client_port"]['cont'])
|
||||
)
|
||||
|
||||
etcd_machines_str = " ".join(["%s:%d" % (h, p) for h, p in etcd_machines])
|
||||
LOG.debug("Using the following etcd urls: \"%s\"", etcd_machines_str)
|
||||
|
||||
return etcd.Client(host=tuple(etcd_machines), allow_reconnect=True,
|
||||
read_timeout=2)
|
||||
read_timeout=2, protocol=scheme, ca_cert=ca_cert)
|
||||
|
||||
|
||||
def check_dependence(dep, etcd_client):
|
||||
|
@ -465,6 +481,16 @@ def get_variables(role_name):
|
|||
return variables
|
||||
|
||||
|
||||
def _get_ca_certificate():
|
||||
name = CACERT
|
||||
if not os.path.isfile(name):
|
||||
with open(CACERT, 'w') as f:
|
||||
f.write(VARIABLES['security']['tls']['ca_cert'])
|
||||
LOG.info("CA certificated saved to %s", CACERT)
|
||||
else:
|
||||
LOG.info("CA file exists, not overwriting it")
|
||||
|
||||
|
||||
def main():
|
||||
action_parser = argparse.ArgumentParser(add_help=False)
|
||||
action_parser.add_argument("action")
|
||||
|
@ -476,6 +502,8 @@ def main():
|
|||
VARIABLES = get_variables(args.role)
|
||||
LOG.debug('Global variables:\n%s', VARIABLES)
|
||||
|
||||
if VARIABLES["security"]["tls"]["enabled"]:
|
||||
_get_ca_certificate()
|
||||
if args.action == "provision":
|
||||
do_provision(args.role)
|
||||
elif args.action == "status":
|
||||
|
|
|
@ -142,6 +142,11 @@ class TestGetETCDClient(base.TestCase):
|
|||
"private": {
|
||||
"address": "192.0.2.1"
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"tls": {
|
||||
"enabled": False
|
||||
}
|
||||
}
|
||||
}
|
||||
with mock.patch("etcd.Client") as m_etcd:
|
||||
|
@ -152,7 +157,9 @@ class TestGetETCDClient(base.TestCase):
|
|||
m_etcd.assert_called_once_with(
|
||||
host=(("192.0.2.1", 10042),),
|
||||
allow_reconnect=True,
|
||||
read_timeout=2)
|
||||
read_timeout=2,
|
||||
protocol='http',
|
||||
ca_cert=None)
|
||||
|
||||
def test_get_etcd_client(self):
|
||||
start_script.VARIABLES = {
|
||||
|
@ -166,6 +173,11 @@ class TestGetETCDClient(base.TestCase):
|
|||
"connection_attempts": 3,
|
||||
"connection_delay": 0,
|
||||
},
|
||||
"security": {
|
||||
"tls": {
|
||||
"enabled": False
|
||||
}
|
||||
}
|
||||
}
|
||||
with mock.patch("etcd.Client") as m_etcd:
|
||||
expected_value = object()
|
||||
|
@ -175,7 +187,39 @@ class TestGetETCDClient(base.TestCase):
|
|||
m_etcd.assert_called_once_with(
|
||||
host=(('etcd.ccp.svc.cluster.local', 1234),),
|
||||
allow_reconnect=True,
|
||||
read_timeout=2)
|
||||
read_timeout=2,
|
||||
protocol='http',
|
||||
ca_cert=None)
|
||||
|
||||
def test_get_secured_etcd_client(self):
|
||||
start_script.VARIABLES = {
|
||||
"role_name": "banana",
|
||||
"namespace": "ccp",
|
||||
"cluster_domain": 'cluster.local',
|
||||
"etcd": {
|
||||
"client_port": {
|
||||
"cont": 1234
|
||||
},
|
||||
"connection_attempts": 3,
|
||||
"connection_delay": 0,
|
||||
},
|
||||
"security": {
|
||||
"tls": {
|
||||
"enabled": True
|
||||
}
|
||||
}
|
||||
}
|
||||
with mock.patch("etcd.Client") as m_etcd:
|
||||
expected_value = object()
|
||||
m_etcd.return_value = expected_value
|
||||
etcd_client = start_script.get_etcd_client()
|
||||
self.assertIs(expected_value, etcd_client)
|
||||
m_etcd.assert_called_once_with(
|
||||
host=(('etcd.ccp.svc.cluster.local', 1234),),
|
||||
allow_reconnect=True,
|
||||
read_timeout=2,
|
||||
protocol='https',
|
||||
ca_cert='/opt/ccp/etc/tls/ca.pem')
|
||||
|
||||
def test_get_etcd_client_wrong(self):
|
||||
start_script.VARIABLES = {
|
||||
|
|
Loading…
Reference in New Issue