From 28aad43113ca420750ef97a5d28e62d35f4ebf90 Mon Sep 17 00:00:00 2001 From: Proskurin Kirill Date: Thu, 12 Jan 2017 13:43:00 +0000 Subject: [PATCH] Add SSL encryption to galera Change-Id: I9e6d9ee439cab734eba02320d58ccfcd73e23106 --- service/files/ca.pem.j2 | 1 + service/files/defaults.yaml | 2 ++ service/files/galera_checker.py | 31 ++++++++++++++++++++++------- service/files/haproxy_entrypoint.py | 21 +++++++++++++++++-- service/files/my.cnf.j2 | 14 ++++++++++++- service/files/percona_entrypoint.py | 23 +++++++++++++++++++-- service/files/server-cert.pem.j2 | 1 + service/files/server-key.pem.j2 | 1 + service/galera.yaml | 29 +++++++++++++++++++++++++++ 9 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 service/files/ca.pem.j2 create mode 100644 service/files/server-cert.pem.j2 create mode 100644 service/files/server-key.pem.j2 diff --git a/service/files/ca.pem.j2 b/service/files/ca.pem.j2 new file mode 100644 index 0000000..d52069b --- /dev/null +++ b/service/files/ca.pem.j2 @@ -0,0 +1 @@ +{{ security.tls.ca_cert }} diff --git a/service/files/defaults.yaml b/service/files/defaults.yaml index 6deee0e..aa21508 100644 --- a/service/files/defaults.yaml +++ b/service/files/defaults.yaml @@ -16,6 +16,8 @@ configs: node: null port: cont: 3306 + tls: + enabled: true url: percona: debian: diff --git a/service/files/galera_checker.py b/service/files/galera_checker.py index e3134a8..51583cc 100644 --- a/service/files/galera_checker.py +++ b/service/files/galera_checker.py @@ -33,11 +33,16 @@ PID_FILE = os.path.join(DATADIR, "mysqld.pid") HOSTNAME = socket.getfqdn() IPADDR = socket.gethostbyname(HOSTNAME) +CA_CERT = '/opt/ccp/etc/tls/ca.pem' +SERVER_CERT = '/opt/ccp/etc/tls/server-cert.pem' +SERVER_KEY = '/opt/ccp/etc/tls/server-key.pem' + MONITOR_PASSWORD = None CLUSTER_NAME = None ETCD_PATH = None ETCD_HOST = None ETCD_PORT = None +TLS = None def retry(f): @@ -64,11 +69,22 @@ def retry(f): def get_etcd_client(): - etcd_client = etcd.Client(host=ETCD_HOST, - port=ETCD_PORT, - allow_reconnect=True, - read_timeout=2) - return etcd_client + if TLS: + protocol = 'https' + cert = (SERVER_CERT, SERVER_KEY) + ca_cert = CA_CERT + else: + protocol = 'http' + cert = None + ca_cert = None + + return etcd.Client(host=ETCD_HOST, + port=ETCD_PORT, + allow_reconnect=True, + protocol=protocol, + cert=cert, + ca_cert=ca_cert, + read_timeout=2) @retry @@ -287,7 +303,7 @@ def get_config(): variables = {} with open(GLOBALS_PATH) as f: global_conf = json.load(f) - for key in ['percona', 'etcd', 'namespace', 'cluster_domain']: + for key in ['percona', 'etcd', 'namespace', 'cluster_domain', 'security']: variables[key] = global_conf[key] LOG.debug(variables) return variables @@ -297,7 +313,7 @@ def set_globals(): config = get_config() global MONITOR_PASSWORD, CLUSTER_NAME - global ETCD_PATH, ETCD_HOST, ETCD_PORT + global ETCD_PATH, ETCD_HOST, ETCD_PORT, TLS CLUSTER_NAME = config['percona']['cluster_name'] MONITOR_PASSWORD = config['percona']['monitor_password'] @@ -305,6 +321,7 @@ def set_globals(): ETCD_HOST = "etcd.%s.svc.%s" % (config['namespace'], config['cluster_domain']) ETCD_PORT = int(config['etcd']['client_port']['cont']) + TLS = config['security']['tls']['enabled'] if __name__ == "__main__": diff --git a/service/files/haproxy_entrypoint.py b/service/files/haproxy_entrypoint.py index afc3744..67694db 100644 --- a/service/files/haproxy_entrypoint.py +++ b/service/files/haproxy_entrypoint.py @@ -18,6 +18,10 @@ BACKEND_NAME = "galera-cluster" SERVER_NAME = "primary" GLOBALS_PATH = '/etc/ccp/globals/globals.json' +CA_CERT = '/opt/ccp/etc/tls/ca.pem' +SERVER_CERT = '/opt/ccp/etc/tls/server-cert.pem' +SERVER_KEY = '/opt/ccp/etc/tls/server-key.pem' + LOG_DATEFMT = "%Y-%m-%d %H:%M:%S" LOG_FORMAT = "%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s" logging.basicConfig(format=LOG_FORMAT, datefmt=LOG_DATEFMT) @@ -58,7 +62,7 @@ def get_config(): variables = {} with open(GLOBALS_PATH) as f: global_conf = json.load(f) - for key in ['percona', 'etcd', 'namespace', 'cluster_domain']: + for key in ['percona', 'etcd', 'namespace', 'cluster_domain', 'security']: variables[key] = global_conf[key] LOG.debug(variables) return variables @@ -68,7 +72,7 @@ def set_globals(): config = get_config() global CONNECTION_ATTEMPTS, CONNECTION_DELAY - global ETCD_PATH, ETCD_HOST, ETCD_PORT + global ETCD_PATH, ETCD_HOST, ETCD_PORT, TLS CONNECTION_ATTEMPTS = config['etcd']['connection_attempts'] CONNECTION_DELAY = config['etcd']['connection_delay'] @@ -76,13 +80,26 @@ def set_globals(): ETCD_HOST = "etcd.%s.svc.%s" % (config['namespace'], config['cluster_domain']) ETCD_PORT = int(config['etcd']['client_port']['cont']) + TLS = config['security']['tls']['enabled'] def get_etcd_client(): + if TLS: + protocol = 'https' + cert = (SERVER_CERT, SERVER_KEY) + ca_cert = CA_CERT + else: + protocol = 'http' + cert = None + ca_cert = None + return etcd.Client(host=ETCD_HOST, port=ETCD_PORT, allow_reconnect=True, + protocol=protocol, + cert=cert, + ca_cert=ca_cert, read_timeout=2) diff --git a/service/files/my.cnf.j2 b/service/files/my.cnf.j2 index ebfc3ce..cd4f386 100644 --- a/service/files/my.cnf.j2 +++ b/service/files/my.cnf.j2 @@ -35,4 +35,16 @@ wsrep_provider = /usr/lib/galera3/libgalera_smm.so wsrep_cluster_name = {{ percona.cluster_name }} wsrep_sst_method = xtrabackup-v2 wsrep_sst_auth = "xtrabackup:{{ percona.xtrabackup_password }}" -wsrep_provider_options = "gcache.size={{ percona.gcache_size }};gcache.recover=yes" +wsrep_provider_options = "gcache.size={{ percona.gcache_size }};gcache.recover=yes{% if percona.tls.enabled and security.tls.enabled %};socket.ssl=yes;socket.ssl_key=/opt/ccp/etc/tls/server-key.pem;socket.ssl_cert=/opt/ccp/etc/tls/server-cert.pem;socket.ssl_ca=/opt/ccp/etc/tls/ca.pem"{% endif %} + +{% if percona.tls.enabled and security.tls.enabled %} +ssl-ca = /opt/ccp/etc/tls/ca.pem +ssl-cert = /opt/ccp/etc/tls/server-cert.pem +ssl-key = /opt/ccp/etc/tls/server-key.pem + +[sst] +encrypt = 4 +ssl-ca = /opt/ccp/etc/tls/ca.pem +ssl-cert = /opt/ccp/etc/tls/server-cert.pem +ssl-key = /opt/ccp/etc/tls/server-key.pem +{% endif %} diff --git a/service/files/percona_entrypoint.py b/service/files/percona_entrypoint.py index 5a1f73d..f6c629c 100644 --- a/service/files/percona_entrypoint.py +++ b/service/files/percona_entrypoint.py @@ -26,6 +26,10 @@ GRASTATE_FILE = os.path.join(DATADIR, 'grastate.dat') SST_FLAG = os.path.join(DATADIR, "sst_in_progress") GLOBALS_PATH = '/etc/ccp/globals/globals.json' +CA_CERT = '/opt/ccp/etc/tls/ca.pem' +SERVER_CERT = '/opt/ccp/etc/tls/server-cert.pem' +SERVER_KEY = '/opt/ccp/etc/tls/server-key.pem' + LOG_DATEFMT = "%Y-%m-%d %H:%M:%S" LOG_FORMAT = "%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s" logging.basicConfig(format=LOG_FORMAT, datefmt=LOG_DATEFMT) @@ -44,6 +48,7 @@ CONNECTION_DELAY = None ETCD_PATH = None ETCD_HOST = None ETCD_PORT = None +TLS = None class ProcessException(Exception): @@ -76,7 +81,8 @@ def get_config(): variables = {} with open(GLOBALS_PATH) as f: global_conf = json.load(f) - for key in ['percona', 'db', 'etcd', 'namespace', 'cluster_domain']: + for key in ['percona', 'db', 'etcd', 'namespace', 'cluster_domain', + 'security']: variables[key] = global_conf[key] LOG.debug(variables) return variables @@ -88,7 +94,7 @@ def set_globals(): global MYSQL_ROOT_PASSWORD, CLUSTER_NAME, XTRABACKUP_PASSWORD global MONITOR_PASSWORD, CONNECTION_ATTEMPTS, CONNECTION_DELAY global ETCD_PATH, ETCD_HOST, ETCD_PORT, EXPECTED_NODES - global FORCE_BOOTSTRAP, FORCE_BOOTSTRAP_NODE + global FORCE_BOOTSTRAP, FORCE_BOOTSTRAP_NODE, TLS FORCE_BOOTSTRAP = config['percona']['force_bootstrap']['enabled'] FORCE_BOOTSTRAP_NODE = config['percona']['force_bootstrap']['node'] @@ -103,6 +109,7 @@ def set_globals(): ETCD_HOST = "etcd.%s.svc.%s" % (config['namespace'], config['cluster_domain']) ETCD_PORT = int(config['etcd']['client_port']['cont']) + TLS = config['security']['tls']['enabled'] def get_mysql_client(insecure=False): @@ -118,9 +125,21 @@ def get_mysql_client(insecure=False): def get_etcd_client(): + if TLS: + protocol = 'https' + cert = (SERVER_CERT, SERVER_KEY) + ca_cert = CA_CERT + else: + protocol = 'http' + cert = None + ca_cert = None + return etcd.Client(host=ETCD_HOST, port=ETCD_PORT, allow_reconnect=True, + protocol=protocol, + cert=cert, + ca_cert=ca_cert, read_timeout=2) diff --git a/service/files/server-cert.pem.j2 b/service/files/server-cert.pem.j2 new file mode 100644 index 0000000..8abc152 --- /dev/null +++ b/service/files/server-cert.pem.j2 @@ -0,0 +1 @@ +{{ security.tls.server_cert }} diff --git a/service/files/server-key.pem.j2 b/service/files/server-key.pem.j2 new file mode 100644 index 0000000..70cf751 --- /dev/null +++ b/service/files/server-key.pem.j2 @@ -0,0 +1 @@ +{{ security.tls.server_key }} diff --git a/service/galera.yaml b/service/galera.yaml index 1cf0438..7421398 100644 --- a/service/galera.yaml +++ b/service/galera.yaml @@ -15,6 +15,11 @@ service: daemon: files: - galera-checker + # {% if percona.tls.enabled %} + - ca.pem + - server-key.pem + - server-cert.pem + # {% endif %} dependencies: - etcd command: "/opt/ccp/bin/galera_checker.py" @@ -31,6 +36,11 @@ service: files: - haproxy-conf - haproxy_entrypoint + # {% if percona.tls.enabled %} + - ca.pem + - server-key.pem + - server-cert.pem + # {% endif %} dependencies: - etcd command: "/opt/ccp/bin/haproxy_entrypoint.py daemon" @@ -67,6 +77,11 @@ service: - entrypoint - mycnf - galera-checker + # {% if percona.tls.enabled %} + - ca.pem + - server-key.pem + - server-cert.pem + # {% endif %} dependencies: - etcd command: /opt/ccp/bin/entrypoint.py @@ -90,3 +105,17 @@ files: path: /opt/ccp/bin/haproxy_entrypoint.py content: haproxy_entrypoint.py perm: "0755" +# {% if percona.tls.enabled %} + ca.pem: + path: /opt/ccp/etc/tls/ca.pem + content: ca.pem.j2 + perm: "0400" + server-key.pem: + path: /opt/ccp/etc/tls/server-key.pem + content: server-key.pem.j2 + perm: "0400" + server-cert.pem: + path: /opt/ccp/etc/tls/server-cert.pem + content: server-cert.pem.j2 + perm: "0400" +# {% endif %}