diff --git a/README.md b/README.md index 0ceaedf..063acde 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,18 @@ # contrail-introspect-cli + +## SSL Support +This is a very slightly modified fork of https://github.com/vcheny/contrail-introspect-cli +The modifications are to add support for SSL enabled introspect endpoints. +When SSL is enabled we need to use SSL/TLS Client Authentication. The modifications allow passing of client side cert/key pair and a CA. +```set_ssl_envs``` contains example environment variable settings. +Alternatively use ```--key_file, --ca_file & --cert_file``` to pass as CLI arguments. +see ```ist.py --help``` for more details. + +If easyRSA (e.g. via juju charm) has been used to generate CA & certs for the introspect APIs, the ```set_ssl_envs``` file and the referenced certs & key can be generated with https://github.com/dannyvernals/contrail-gen-introspect-cert + +The rest of the README is verbatim from https://github.com/vcheny/contrail-introspect-cli + +## Introduction This script provides a Contrail CLI command mainly for troublelshooting prupose. It retrieves XML output from introspect services provided by Contrail main components e.g. control, config and comptue(vrouter) nodes and makes them CLI friendly. Contrail 2.22+ is supported. diff --git a/clear_ssl_vars b/clear_ssl_vars new file mode 100644 index 0000000..324fd58 --- /dev/null +++ b/clear_ssl_vars @@ -0,0 +1,4 @@ +export SSL_KEY_FILE="" +export SSL_CA_FILE="" +export SSL_CERT_FILE="" + diff --git a/ist.py b/ist.py index efc2b8e..b05a575 100755 --- a/ist.py +++ b/ist.py @@ -46,9 +46,14 @@ } class Introspect: - def __init__ (self, host, port, filename): - - self.host_url = "http://" + host + ":" + str(port) + "/" + def __init__ (self, host, port, filename, ssl_vars): + if ssl_vars["cert_file"]: + self.host_url = "https://" + host + ":" + str(port) + "/" + self.ssl_vars = ssl_vars + self.has_ssl = True + else: + self.host_url = "http://" + host + ":" + str(port) + "/" + self.has_ssl = False self.filename = filename def get (self, path): @@ -73,7 +78,13 @@ def get (self, path): headers['X-Auth-Token'] = token if debug: print "DEBUG: retrieving url " + url try: - response = requests.get(url,headers=headers) + if self.has_ssl: + response = requests.get(url,headers=headers, + verify=self.ssl_vars["ca_file"], + cert=(self.ssl_vars["cert_file"], self.ssl_vars["key_file"]) + ) + else: + response = requests.get(url,headers=headers) response.raise_for_status() except requests.exceptions.HTTPError: print 'The server couldn\'t fulfill the request.' @@ -582,7 +593,7 @@ class CLI_basic(object): common_parser.add_argument('--max_width', type=int, help="Max width per column") - def __init__(self, parser, host, port, filename): + def __init__(self, parser, host, port, filename, ssl_vars): host = host or '127.0.0.1' if port is None: @@ -592,7 +603,7 @@ def __init__(self, parser, host, port, filename): except: port = self.IntrospectPortMap[ServiceMap[cli_type] + ':0'] - self.IST = Introspect(host, port, filename) + self.IST = Introspect(host, port, filename, ssl_vars) self.subparser = parser.add_subparsers() @@ -696,8 +707,8 @@ class CLI_nodemgr_analytics(CLI_basic): class CLI_cfg_schema(CLI_basic): - def __init__(self, parser, host, port, filename): - CLI_basic.__init__(self, parser, host, port, filename) + def __init__(self, parser, host, port, filename, ssl_vars): + CLI_basic.__init__(self, parser, host, port, filename, ssl_vars) self.add_parse_args() def add_parse_args(self): @@ -760,8 +771,8 @@ def SnhStObjectReq(self, args): class CLI_cfg_svcmon(CLI_basic): - def __init__(self, parser, host, port, filename): - CLI_basic.__init__(self, parser, host, port, filename) + def __init__(self, parser, host, port, filename, ssl_vars): + CLI_basic.__init__(self, parser, host, port, filename, ssl_vars) self.add_parse_args() def add_parse_args(self): @@ -782,8 +793,8 @@ def SnhServiceInstanceList(self, args): class CLI_ctr(CLI_basic): - def __init__(self, parser, host, port, filename): - CLI_basic.__init__(self, parser, host, port, filename) + def __init__(self, parser, host, port, filename, ssl_vars): + CLI_basic.__init__(self, parser, host, port, filename, ssl_vars) self.add_parse_args() def add_parse_args(self): @@ -1302,8 +1313,8 @@ def SnhShowRoute(self, args): self.IST.showRoute_CTR(args.last, mode) class CLI_vr(CLI_basic): - def __init__(self, parser, host, port, filename): - CLI_basic.__init__(self, parser, host, port, filename) + def __init__(self, parser, host, port, filename, ssl_vars): + CLI_basic.__init__(self, parser, host, port, filename, ssl_vars) self.add_parse_args() def add_parse_args(self): @@ -1843,8 +1854,8 @@ def SnhShowIFMap(self, args): class CLI_collector(CLI_basic): - def __init__(self, parser, host, port, filename): - CLI_basic.__init__(self, parser, host, port, filename) + def __init__(self, parser, host, port, filename, ssl_vars): + CLI_basic.__init__(self, parser, host, port, filename, ssl_vars) self.add_parse_args() def add_parse_args(self): @@ -1997,22 +2008,38 @@ def main(): if '--debug' in argv: debug = True + ssl_vars = dict() + try: + ssl_vars['key_file'] = argv[argv.index('--key_file') + 1] + except ValueError: + ssl_vars['key_file'] = os.environ.get('SSL_KEY_FILE', None) + try: + ssl_vars['ca_file'] = argv[argv.index('--ca_file') + 1] + except ValueError: + ssl_vars['ca_file'] = os.environ.get('SSL_CA_FILE', None) + try: + ssl_vars['cert_file'] = argv[argv.index('--cert_file') + 1] + except ValueError: + ssl_vars['cert_file'] = os.environ.get('SSL_CERT_FILE', None) parser = argparse.ArgumentParser(prog='ist', description='A script to make Contrail Introspect output CLI friendly.') - parser.add_argument('--version', action="store_true", help="Script version") - parser.add_argument('--debug', action="store_true", help="Verbose mode") - parser.add_argument('--host', type=str, help="Introspect host address. Default: localhost") - parser.add_argument('--port', type=int, help="Introspect port number") - parser.add_argument('--proxy', type=str, help="Introspect proxy URL") - parser.add_argument('--token', type=str, help="Token for introspect proxy requests") - parser.add_argument('--file', type=str, help="Introspect file") + parser.add_argument('--version', action="store_true", help="Script version") + parser.add_argument('--debug', action="store_true", help="Verbose mode") + parser.add_argument('--host', type=str, help="Introspect host address. Default: localhost") + parser.add_argument('--port', type=int, help="Introspect port number") + parser.add_argument('--proxy', type=str, help="Introspect proxy URL") + parser.add_argument('--token', type=str, help="Token for introspect proxy requests") + parser.add_argument('--file', type=str, help="Introspect file") + parser.add_argument('--key_file', type=str, help="SSL key file, alternatively set SSL_KEY_FILE env var") + parser.add_argument('--ca_file', type=str, help="SSL ca file, alternatively set SSL_CA_FILE env var") + parser.add_argument('--cert_file', type=str, help="SSL cert file, alternatively set SSL_CERT_FILE env var") roleparsers = parser.add_subparsers() for svc in sorted(ServiceMap.iterkeys()): p = roleparsers.add_parser(svc, help=ServiceMap[svc]) if 'CLI_%s' % (svc) in globals(): - globals()['CLI_%s' % (svc)](p, host, port, filename) + globals()['CLI_%s' % (svc)](p, host, port, filename, ssl_vars) args, unknown = parser.parse_known_args() args.func(args) diff --git a/set_ssl_envs b/set_ssl_envs new file mode 100644 index 0000000..40f0826 --- /dev/null +++ b/set_ssl_envs @@ -0,0 +1,3 @@ +export SSL_KEY_FILE="/etc/contrail/ssl/private/server-privkey.pem" +export SSL_CA_FILE="/etc/contrail/ssl/certs/ca-cert.pem" +export SSL_CERT_FILE="/etc/contrail/ssl/certs/server.pem"