Add new jumphost feature to nodes
bug fix when adding a node without name
This commit is contained in:
		| @@ -1,2 +1,2 @@ | |||||||
| __version__ = "3.6.4" | __version__ = "3.7.0b1" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ class configfile: | |||||||
|  |  | ||||||
|     def _createconfig(self, conf): |     def _createconfig(self, conf): | ||||||
|         #Create config file |         #Create config file | ||||||
|         defaultconfig = {'config': {'case': False, 'idletime': 30, 'fzf': False}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"", "tags": "" }}} |         defaultconfig = {'config': {'case': False, 'idletime': 30, 'fzf': False}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"", "tags": "", "jumphost":""}}} | ||||||
|         if not os.path.exists(conf): |         if not os.path.exists(conf): | ||||||
|             with open(conf, "w") as f: |             with open(conf, "w") as f: | ||||||
|                 json.dump(defaultconfig, f, indent = 4) |                 json.dump(defaultconfig, f, indent = 4) | ||||||
| @@ -238,14 +238,14 @@ class configfile: | |||||||
|         return nodes |         return nodes | ||||||
|  |  | ||||||
|  |  | ||||||
|     def _connections_add(self,*, id, host, folder='', subfolder='', options='', logs='', password='', port='', protocol='', user='', tags='', type = "connection" ): |     def _connections_add(self,*, id, host, folder='', subfolder='', options='', logs='', password='', port='', protocol='', user='', tags='', jumphost='', type = "connection" ): | ||||||
|         #Add connection from config |         #Add connection from config | ||||||
|         if folder == '': |         if folder == '': | ||||||
|             self.connections[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags,"type": type} |             self.connections[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags,"jumphost": jumphost,"type": type} | ||||||
|         elif folder != '' and subfolder == '': |         elif folder != '' and subfolder == '': | ||||||
|             self.connections[folder][id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags, "type": type} |             self.connections[folder][id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags, "jumphost": jumphost, "type": type} | ||||||
|         elif folder != '' and subfolder != '': |         elif folder != '' and subfolder != '': | ||||||
|             self.connections[folder][subfolder][id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags, "type": type} |             self.connections[folder][subfolder][id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags,  "jumphost": jumphost, "type": type} | ||||||
|              |              | ||||||
|  |  | ||||||
|     def _connections_del(self,*, id, folder='', subfolder=''): |     def _connections_del(self,*, id, folder='', subfolder=''): | ||||||
| @@ -274,9 +274,9 @@ class configfile: | |||||||
|             del self.connections[folder][subfolder] |             del self.connections[folder][subfolder] | ||||||
|  |  | ||||||
|  |  | ||||||
|     def _profiles_add(self,*, id, host = '', options='', logs='', password='', port='', protocol='', user='', tags='' ): |     def _profiles_add(self,*, id, host = '', options='', logs='', password='', port='', protocol='', user='', tags='', jumphost='' ): | ||||||
|         #Add profile from config |         #Add profile from config | ||||||
|         self.profiles[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags} |         self.profiles[id] = {"host": host, "options": options, "logs": logs, "password": password, "port": port, "protocol": protocol, "user": user, "tags": tags, "jumphost": jumphost} | ||||||
|              |              | ||||||
|  |  | ||||||
|     def _profiles_del(self,*, id ): |     def _profiles_del(self,*, id ): | ||||||
|   | |||||||
| @@ -539,6 +539,7 @@ class connapp: | |||||||
|             newnode["options"] = newnodes["options"] |             newnode["options"] = newnodes["options"] | ||||||
|             newnode["logs"] = newnodes["logs"] |             newnode["logs"] = newnodes["logs"] | ||||||
|             newnode["tags"] = newnodes["tags"] |             newnode["tags"] = newnodes["tags"] | ||||||
|  |             newnode["jumphost"] = newnodes["jumphost"] | ||||||
|             newnode["user"] = newnodes["user"] |             newnode["user"] = newnodes["user"] | ||||||
|             newnode["password"] = newnodes["password"] |             newnode["password"] = newnodes["password"] | ||||||
|             count +=1 |             count +=1 | ||||||
| @@ -992,6 +993,23 @@ class connapp: | |||||||
|                 raise inquirer.errors.ValidationError("", reason="Tags should be a python dictionary.".format(current)) |                 raise inquirer.errors.ValidationError("", reason="Tags should be a python dictionary.".format(current)) | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
|  |     def _jumphost_validation(self, answers, current): | ||||||
|  |         #Validation for Jumphost in inquirer when managing nodes | ||||||
|  |         if current.startswith("@"): | ||||||
|  |             if current[1:] not in self.profiles: | ||||||
|  |                 raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current)) | ||||||
|  |         elif current != "": | ||||||
|  |             if current not in self.nodes : | ||||||
|  |                 raise inquirer.errors.ValidationError("", reason="Node {} don't exist.".format(current)) | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     def _profile_jumphost_validation(self, answers, current): | ||||||
|  |         #Validation for Jumphost in inquirer when managing profiles | ||||||
|  |         if current != "": | ||||||
|  |             if current not in self.nodes : | ||||||
|  |                 raise inquirer.errors.ValidationError("", reason="Node {} don't exist.".format(current)) | ||||||
|  |         return True | ||||||
|  |  | ||||||
|     def _default_validation(self, answers, current): |     def _default_validation(self, answers, current): | ||||||
|         #Default validation type used in multiples questions in inquirer |         #Default validation type used in multiples questions in inquirer | ||||||
|         if current.startswith("@"): |         if current.startswith("@"): | ||||||
| @@ -1039,6 +1057,7 @@ class connapp: | |||||||
|         questions.append(inquirer.Confirm("options", message="Edit Options?")) |         questions.append(inquirer.Confirm("options", message="Edit Options?")) | ||||||
|         questions.append(inquirer.Confirm("logs", message="Edit logging path/file?")) |         questions.append(inquirer.Confirm("logs", message="Edit logging path/file?")) | ||||||
|         questions.append(inquirer.Confirm("tags", message="Edit tags?")) |         questions.append(inquirer.Confirm("tags", message="Edit tags?")) | ||||||
|  |         questions.append(inquirer.Confirm("jumphost", message="Edit jumphost?")) | ||||||
|         questions.append(inquirer.Confirm("user", message="Edit User?")) |         questions.append(inquirer.Confirm("user", message="Edit User?")) | ||||||
|         questions.append(inquirer.Confirm("password", message="Edit password?")) |         questions.append(inquirer.Confirm("password", message="Edit password?")) | ||||||
|         answers = inquirer.prompt(questions) |         answers = inquirer.prompt(questions) | ||||||
| @@ -1050,11 +1069,13 @@ class connapp: | |||||||
|             defaults = self.config.getitem(unique) |             defaults = self.config.getitem(unique) | ||||||
|             if "tags" not in defaults: |             if "tags" not in defaults: | ||||||
|                 defaults["tags"] = "" |                 defaults["tags"] = "" | ||||||
|  |             if "jumphost" not in defaults: | ||||||
|  |                 defaults["jumphost"] = "" | ||||||
|         except: |         except: | ||||||
|             defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"" , "tags":"", "password":""} |             defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"" , "tags":"", "password":"", "jumphost":""} | ||||||
|         node = {} |         node = {} | ||||||
|         if edit == None: |         if edit == None: | ||||||
|             edit = { "host":True, "protocol":True, "port":True, "user":True, "password": True,"options":True, "logs":True, "tags":True } |             edit = { "host":True, "protocol":True, "port":True, "user":True, "password": True,"options":True, "logs":True, "tags":True, "jumphost":True } | ||||||
|         questions = [] |         questions = [] | ||||||
|         if edit["host"]: |         if edit["host"]: | ||||||
|             questions.append(inquirer.Text("host", message="Add Hostname or IP", validate=self._host_validation, default=defaults["host"])) |             questions.append(inquirer.Text("host", message="Add Hostname or IP", validate=self._host_validation, default=defaults["host"])) | ||||||
| @@ -1080,6 +1101,10 @@ class connapp: | |||||||
|             questions.append(inquirer.Text("tags", message="Add tags dictionary",  validate=self._tags_validation, default=str(defaults["tags"]).replace("{","{{").replace("}","}}"))) |             questions.append(inquirer.Text("tags", message="Add tags dictionary",  validate=self._tags_validation, default=str(defaults["tags"]).replace("{","{{").replace("}","}}"))) | ||||||
|         else: |         else: | ||||||
|             node["tags"] = defaults["tags"] |             node["tags"] = defaults["tags"] | ||||||
|  |         if edit["jumphost"]: | ||||||
|  |             questions.append(inquirer.Text("jumphost", message="Add Jumphost node",  validate=self._jumphost_validation, default=str(defaults["jumphost"]).replace("{","{{").replace("}","}}"))) | ||||||
|  |         else: | ||||||
|  |             node["jumphost"] = defaults["jumphost"] | ||||||
|         if edit["user"]: |         if edit["user"]: | ||||||
|             questions.append(inquirer.Text("user", message="Pick username", validate=self._default_validation, default=defaults["user"])) |             questions.append(inquirer.Text("user", message="Pick username", validate=self._default_validation, default=defaults["user"])) | ||||||
|         else: |         else: | ||||||
| @@ -1118,11 +1143,13 @@ class connapp: | |||||||
|             defaults = self.config.profiles[unique] |             defaults = self.config.profiles[unique] | ||||||
|             if "tags" not in defaults: |             if "tags" not in defaults: | ||||||
|                 defaults["tags"] = "" |                 defaults["tags"] = "" | ||||||
|  |             if "jumphost" not in defaults: | ||||||
|  |                 defaults["jumphost"] = "" | ||||||
|         except: |         except: | ||||||
|             defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"", "tags": "" } |             defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"", "tags": "", "jumphost": ""} | ||||||
|         profile = {} |         profile = {} | ||||||
|         if edit == None: |         if edit == None: | ||||||
|             edit = { "host":True, "protocol":True, "port":True, "user":True, "password": True,"options":True, "logs":True, "tags":True } |             edit = { "host":True, "protocol":True, "port":True, "user":True, "password": True,"options":True, "logs":True, "tags":True, "jumphost":True } | ||||||
|         questions = [] |         questions = [] | ||||||
|         if edit["host"]: |         if edit["host"]: | ||||||
|             questions.append(inquirer.Text("host", message="Add Hostname or IP", default=defaults["host"])) |             questions.append(inquirer.Text("host", message="Add Hostname or IP", default=defaults["host"])) | ||||||
| @@ -1148,6 +1175,10 @@ class connapp: | |||||||
|             questions.append(inquirer.Text("tags", message="Add tags dictionary",  validate=self._profile_tags_validation, default=str(defaults["tags"]).replace("{","{{").replace("}","}}"))) |             questions.append(inquirer.Text("tags", message="Add tags dictionary",  validate=self._profile_tags_validation, default=str(defaults["tags"]).replace("{","{{").replace("}","}}"))) | ||||||
|         else: |         else: | ||||||
|             profile["tags"] = defaults["tags"] |             profile["tags"] = defaults["tags"] | ||||||
|  |         if edit["jumphost"]: | ||||||
|  |             questions.append(inquirer.Text("jumphost", message="Add Jumphost node",  validate=self._profile_jumphost_validation, default=str(defaults["jumphost"]).replace("{","{{").replace("}","}}"))) | ||||||
|  |         else: | ||||||
|  |             profile["jumphost"] = defaults["jumphost"] | ||||||
|         if edit["user"]: |         if edit["user"]: | ||||||
|             questions.append(inquirer.Text("user", message="Pick username", default=defaults["user"])) |             questions.append(inquirer.Text("user", message="Pick username", default=defaults["user"])) | ||||||
|         else: |         else: | ||||||
| @@ -1179,6 +1210,7 @@ class connapp: | |||||||
|         questions.append(inquirer.Text("options", message="Pass extra options to protocol", validate=self._default_validation)) |         questions.append(inquirer.Text("options", message="Pass extra options to protocol", validate=self._default_validation)) | ||||||
|         questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self._default_validation)) |         questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self._default_validation)) | ||||||
|         questions.append(inquirer.Text("tags", message="Add tags dictionary",  validate=self._tags_validation)) |         questions.append(inquirer.Text("tags", message="Add tags dictionary",  validate=self._tags_validation)) | ||||||
|  |         questions.append(inquirer.Text("jumphost", message="Add Jumphost node",  validate=self._jumphost_validation)) | ||||||
|         questions.append(inquirer.Text("user", message="Pick username", validate=self._default_validation)) |         questions.append(inquirer.Text("user", message="Pick username", validate=self._default_validation)) | ||||||
|         questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"])) |         questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"])) | ||||||
|         answer = inquirer.prompt(questions) |         answer = inquirer.prompt(questions) | ||||||
| @@ -1201,6 +1233,8 @@ class connapp: | |||||||
|         return answer |         return answer | ||||||
|  |  | ||||||
|     def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")): |     def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")): | ||||||
|  |         if arg_value == None: | ||||||
|  |             raise ValueError("Missing argument node") | ||||||
|         if not pat.match(arg_value): |         if not pat.match(arg_value): | ||||||
|             raise ValueError(f"Argument error: {arg_value}") |             raise ValueError(f"Argument error: {arg_value}") | ||||||
|         return arg_value |         return arg_value | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ class node: | |||||||
|  |  | ||||||
|         ''' |         ''' | ||||||
|      |      | ||||||
|     def __init__(self, unique, host, options='', logs='', password='', port='', protocol='', user='', config='', tags=''): |     def __init__(self, unique, host, options='', logs='', password='', port='', protocol='', user='', config='', tags='', jumphost=''): | ||||||
|         '''  |         '''  | ||||||
|              |              | ||||||
|         ### Parameters:   |         ### Parameters:   | ||||||
| @@ -66,6 +66,8 @@ class node: | |||||||
|  |  | ||||||
|             - tags   (dict) : Tags useful for automation and personal porpuse |             - tags   (dict) : Tags useful for automation and personal porpuse | ||||||
|                               like "os", "prompt" and "screenleght_command" |                               like "os", "prompt" and "screenleght_command" | ||||||
|  |                                | ||||||
|  |             - jumphost (str): Reference another node to be used as a jumphost | ||||||
|         ''' |         ''' | ||||||
|         if config == '': |         if config == '': | ||||||
|             self.idletime = 0 |             self.idletime = 0 | ||||||
| @@ -74,7 +76,7 @@ class node: | |||||||
|             self.idletime = config.config["idletime"] |             self.idletime = config.config["idletime"] | ||||||
|             self.key = config.key |             self.key = config.key | ||||||
|         self.unique = unique |         self.unique = unique | ||||||
|         attr = {"host": host, "logs": logs, "options":options, "port": port, "protocol": protocol, "user": user, "tags": tags} |         attr = {"host": host, "logs": logs, "options":options, "port": port, "protocol": protocol, "user": user, "tags": tags, "jumphost": jumphost} | ||||||
|         for key in attr: |         for key in attr: | ||||||
|             profile = re.search("^@(.*)", str(attr[key])) |             profile = re.search("^@(.*)", str(attr[key])) | ||||||
|             if profile and config != '': |             if profile and config != '': | ||||||
| @@ -97,6 +99,45 @@ class node: | |||||||
|                     self.password.append(config.profiles[profile.group(1)]["password"]) |                     self.password.append(config.profiles[profile.group(1)]["password"]) | ||||||
|         else: |         else: | ||||||
|             self.password = [password] |             self.password = [password] | ||||||
|  |         if self.jumphost != "" and config != '': | ||||||
|  |             self.jumphost = config.getitem(self.jumphost) | ||||||
|  |             for key in self.jumphost: | ||||||
|  |                 profile = re.search("^@(.*)", str(self.jumphost[key])) | ||||||
|  |                 if profile: | ||||||
|  |                     try: | ||||||
|  |                         self.jumphost[key] = config.profiles[profile.group(1)][key] | ||||||
|  |                     except: | ||||||
|  |                         self.jumphost[key] = "" | ||||||
|  |                 elif self.jumphost[key] == '' and key == "protocol": | ||||||
|  |                     try: | ||||||
|  |                         self.jumphost[key] = config.profiles["default"][key] | ||||||
|  |                     except: | ||||||
|  |                         self.jumphost[key] = "ssh" | ||||||
|  |             if isinstance(self.jumphost["password"],list): | ||||||
|  |                 jumphost_password = [] | ||||||
|  |                 for i, s in enumerate(self.jumphost["password"]): | ||||||
|  |                     profile = re.search("^@(.*)", self.jumphost["password"][i]) | ||||||
|  |                     if profile: | ||||||
|  |                         jumphost_password.append(config.profiles[profile.group(1)]["password"]) | ||||||
|  |                 self.jumphost["password"] = jumphost_password | ||||||
|  |             else: | ||||||
|  |                 self.jumphost["password"] = [self.jumphost["password"]] | ||||||
|  |             if self.jumphost["password"] != [""]: | ||||||
|  |                 self.password = self.jumphost["password"] + self.password | ||||||
|  |  | ||||||
|  |             if self.jumphost["protocol"] == "ssh": | ||||||
|  |                 jumphost_cmd = self.jumphost["protocol"] + " -W %h:%p" | ||||||
|  |                 if self.port != '': | ||||||
|  |                     jumphost_cmd = jumphost_cmd + " -p " + self.jumphost["port"] | ||||||
|  |                 if self.options != '': | ||||||
|  |                     jumphost_cmd = jumphost_cmd + " " + self.jumphost["options"] | ||||||
|  |                 if self.user == '': | ||||||
|  |                     jumphost_cmd = jumphost_cmd + " {}".format(self.jumphost["host"]) | ||||||
|  |                 else: | ||||||
|  |                     jumphost_cmd = jumphost_cmd + " {}".format("@".join([self.jumphost["user"],self.jumphost["host"]])) | ||||||
|  |                 self.jumphost = f"-o ProxyCommand=\"{jumphost_cmd}\"" | ||||||
|  |             else: | ||||||
|  |                 self.jumphost = "" | ||||||
|  |  | ||||||
|     def _passtx(self, passwords, *, keyfile=None): |     def _passtx(self, passwords, *, keyfile=None): | ||||||
|         # decrypts passwords, used by other methdos. |         # decrypts passwords, used by other methdos. | ||||||
| @@ -203,8 +244,8 @@ class node: | |||||||
|         ''' |         ''' | ||||||
|         connect = self._connect(debug = debug) |         connect = self._connect(debug = debug) | ||||||
|         if connect == True: |         if connect == True: | ||||||
|             # size = re.search('columns=([0-9]+).*lines=([0-9]+)',str(os.get_terminal_size())) |             size = re.search('columns=([0-9]+).*lines=([0-9]+)',str(os.get_terminal_size())) | ||||||
|             # self.child.setwinsize(int(size.group(2)),int(size.group(1))) |             self.child.setwinsize(int(size.group(2)),int(size.group(1))) | ||||||
|             print("Connected to " + self.unique + " at " + self.host + (":" if self.port != '' else '') + self.port + " via: " + self.protocol) |             print("Connected to " + self.unique + " at " + self.host + (":" if self.port != '' else '') + self.port + " via: " + self.protocol) | ||||||
|             if 'logfile' in dir(self): |             if 'logfile' in dir(self): | ||||||
|                 # Initialize self.mylog |                 # Initialize self.mylog | ||||||
| @@ -431,6 +472,8 @@ class node: | |||||||
|                 cmd = cmd + " " + self.options |                 cmd = cmd + " " + self.options | ||||||
|             if self.logs != '': |             if self.logs != '': | ||||||
|                 self.logfile = self._logfile() |                 self.logfile = self._logfile() | ||||||
|  |             if self.jumphost != '': | ||||||
|  |                 cmd = cmd + " " + self.jumphost | ||||||
|             if self.password[0] != '': |             if self.password[0] != '': | ||||||
|                 passwords = self._passtx(self.password) |                 passwords = self._passtx(self.password) | ||||||
|             else: |             else: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user