Add features:

- New protocols: Docker and Kubectl
 - Add contexts to filter the number of nodes
 - Add option to modify the api using plugins
 - Minor bug fixes
This commit is contained in:
Federico Luzzi 2024-07-15 15:38:01 -03:00
parent a71d8adcb3
commit 4d8244a10f
8 changed files with 320 additions and 141 deletions

3
.gitignore vendored
View File

@ -130,3 +130,6 @@ dmypy.json
#clients #clients
*sync_client* *sync_client*
#App
connpy-completion-helper

View File

@ -9,7 +9,8 @@
[![](https://img.shields.io/pypi/l/connpy.svg?style=flat-square)](https://github.com/fluzzi/connpy/blob/main/LICENSE) [![](https://img.shields.io/pypi/l/connpy.svg?style=flat-square)](https://github.com/fluzzi/connpy/blob/main/LICENSE)
[![](https://img.shields.io/pypi/dm/connpy.svg?style=flat-square)](https://pypi.org/pypi/connpy/) [![](https://img.shields.io/pypi/dm/connpy.svg?style=flat-square)](https://pypi.org/pypi/connpy/)
Connpy is a ssh and telnet connection manager and automation module for Linux, Mac and Docker Connpy is a SSH, SFTP, Telnet, kubectl, and Docker pod connection manager and automation module for Linux, Mac, and Docker.
## Installation ## Installation
@ -43,33 +44,34 @@ Connpy integrates with Google services for backup purposes:
For more detailed information, please read our [Privacy Policy](https://connpy.gederico.dynu.net/fluzzi32/connpy/src/branch/main/PRIVATE_POLICY.md). For more detailed information, please read our [Privacy Policy](https://connpy.gederico.dynu.net/fluzzi32/connpy/src/branch/main/PRIVATE_POLICY.md).
### Features ### Features
- You can generate profiles and reference them from nodes using @profilename so you dont - Manage connections using SSH, SFTP, Telnet, kubectl, and Docker exec.
need to edit multiple nodes when changing password or other information. - Set contexts to manage specific nodes from specific contexts (work/home/clients/etc).
- Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can - You can generate profiles and reference them from nodes using @profilename so you don't
be referenced using node@subfolder@folder or node@folder need to edit multiple nodes when changing passwords or other information.
- If you have too many nodes. Get completion script using: conn config --completion. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. They can
Or use fzf installing pyfzf and running conn config --fzf true be referenced using node@subfolder@folder or node@folder.
- Create in bulk, copy, move, export and import nodes for easy management. - If you have too many nodes, get a completion script using: conn config --completion.
- Run automation scripts in network devices. Or use fzf by installing pyfzf and running conn config --fzf true.
- use GPT AI to help you manage your devices. - Create in bulk, copy, move, export, and import nodes for easy management.
- Run automation scripts on network devices.
- Use GPT AI to help you manage your devices.
- Add plugins with your own scripts. - Add plugins with your own scripts.
- Much more! - Much more!
### Usage: ### Usage:
``` ```
usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp] usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp]
conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config} ... conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config,sync,context} ...
positional arguments: positional arguments:
node|folder node[@subfolder][@folder] node|folder node[@subfolder][@folder]
Connect to specific node or show all matching nodes Connect to specific node or show all matching nodes
[@subfolder][@folder] [@subfolder][@folder]
Show all available connections globaly or in specified path Show all available connections globally or in specified path
```
### Options: options:
```
-h, --help show this help message and exit -h, --help show this help message and exit
-v, --version Show version -v, --version Show version
-a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder
@ -78,10 +80,8 @@ positional arguments:
-s, --show Show node[@subfolder][@folder] -s, --show Show node[@subfolder][@folder]
-d, --debug Display all conections steps -d, --debug Display all conections steps
-t, --sftp Connects using sftp instead of ssh -t, --sftp Connects using sftp instead of ssh
```
### Commands: Commands:
```
profile Manage profiles profile Manage profiles
move(mv) Move node move(mv) Move node
copy(cp) Copy node copy(cp) Copy node
@ -95,6 +95,7 @@ positional arguments:
plugin Manage plugins plugin Manage plugins
config Manage app config config Manage app config
sync Sync config with Google sync Sync config with Google
context Manage contexts with regex matching
``` ```
### Manage profiles: ### Manage profiles:
@ -115,14 +116,26 @@ options:
### Examples: ### Examples:
``` ```
#Add new profile
conn profile --add office-user conn profile --add office-user
#Add new folder
conn --add @office conn --add @office
#Add new subfolder
conn --add @datacenter@office conn --add @datacenter@office
#Add node to subfolder
conn --add server@datacenter@office conn --add server@datacenter@office
#Add node to folder
conn --add pc@office conn --add pc@office
#Show node information
conn --show server@datacenter@office conn --show server@datacenter@office
#Connect to nodes
conn pc@office conn pc@office
conn server conn server
#Create and set new context
conn context -a office .*@office
conn context --set office
#Run a command in a node
conn run server ls -la
``` ```
## Plugin Requirements for Connpy ## Plugin Requirements for Connpy

View File

@ -2,32 +2,35 @@
''' '''
## Connection manager ## Connection manager
Connpy is a connection manager that allows you to store nodes to connect them fast and password free. Connpy is a SSH, SFTP, Telnet, kubectl, and Docker pod connection manager and automation module for Linux, Mac, and Docker.
### Features ### Features
- You can generate profiles and reference them from nodes using @profilename so you dont - Manage connections using SSH, SFTP, Telnet, kubectl, and Docker exec.
need to edit multiple nodes when changing password or other information. - Set contexts to manage specific nodes from specific contexts (work/home/clients/etc).
- Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can - You can generate profiles and reference them from nodes using @profilename so you don't
be referenced using node@subfolder@folder or node@folder need to edit multiple nodes when changing passwords or other information.
- If you have too many nodes. Get completion script using: conn config --completion. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. They can
Or use fzf installing pyfzf and running conn config --fzf true be referenced using node@subfolder@folder or node@folder.
- Create in bulk, copy, move, export and import nodes for easy management. - If you have too many nodes, get a completion script using: conn config --completion.
- Run automation scripts in network devices. Or use fzf by installing pyfzf and running conn config --fzf true.
- use GPT AI to help you manage your devices. - Create in bulk, copy, move, export, and import nodes for easy management.
- Run automation scripts on network devices.
- Use GPT AI to help you manage your devices.
- Add plugins with your own scripts. - Add plugins with your own scripts.
- Much more! - Much more!
### Usage ### Usage
``` ```
usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp] usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp]
conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config} ... conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config,sync,context} ...
positional arguments: positional arguments:
node|folder node[@subfolder][@folder] node|folder node[@subfolder][@folder]
Connect to specific node or show all matching nodes Connect to specific node or show all matching nodes
[@subfolder][@folder] [@subfolder][@folder]
Show all available connections globaly or in specified path Show all available connections globally or in specified path
Options:
options:
-h, --help show this help message and exit -h, --help show this help message and exit
-v, --version Show version -v, --version Show version
-a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder
@ -51,6 +54,7 @@ Commands:
plugin Manage plugins plugin Manage plugins
config Manage app config config Manage app config
sync Sync config with Google sync Sync config with Google
context Manage contexts with regex matching
``` ```
### Manage profiles ### Manage profiles
@ -71,14 +75,26 @@ options:
### Examples ### Examples
``` ```
#Add new profile
conn profile --add office-user conn profile --add office-user
#Add new folder
conn --add @office conn --add @office
#Add new subfolder
conn --add @datacenter@office conn --add @datacenter@office
#Add node to subfolder
conn --add server@datacenter@office conn --add server@datacenter@office
#Add node to folder
conn --add pc@office conn --add pc@office
#Show node information
conn --show server@datacenter@office conn --show server@datacenter@office
#Connect to nodes
conn pc@office conn pc@office
conn server conn server
#Create and set new context
conn context -a office .*@office
conn context --set office
#Run a command in a node
conn run server ls -la
``` ```
## Plugin Requirements for Connpy ## Plugin Requirements for Connpy
### General Structure ### General Structure

View File

@ -1,2 +1,2 @@
__version__ = "4.1.0b3" __version__ = "4.1.0"

View File

@ -41,6 +41,9 @@ class context_manager:
elif context not in self.contexts: elif context not in self.contexts:
print(f"Context {context} doesn't exist.") print(f"Context {context} doesn't exist.")
exit(4) exit(4)
if context == self.current_context:
print(f"Can't delete current context: {self.current_context}")
exit(5)
else: else:
self.contexts.pop(context) self.contexts.pop(context)
self.connapp._change_settings("contexts", self.contexts) self.connapp._change_settings("contexts", self.contexts)
@ -152,3 +155,26 @@ class Entrypoint:
cm.set_context(args.context_name) cm.set_context(args.context_name)
elif args.show: elif args.show:
cm.show_context(args.context_name) cm.show_context(args.context_name)
def _connpy_completion(wordsnumber, words, info=None):
if wordsnumber == 3:
result = ["--help", "--add", "--del", "--rm", "--ls", "--set", "--show", "--edit", "--mod"]
elif wordsnumber == 4 and words[1] in ["--del", "-r", "--rm", "--set", "--edit", "--mod", "-e", "--show", "-s"]:
contexts = info["config"]["config"]["contexts"].keys()
current_context = info["config"]["config"]["current_context"]
default_context = "all"
if words[1] in ["--del", "-r", "--rm"]:
# Filter out default context and current context
result = [context for context in contexts if context not in [default_context, current_context]]
elif words[1] == "--set":
# Filter out current context
result = [context for context in contexts if context != current_context]
elif words[1] in ["--edit", "--mod", "-e"]:
# Filter out default context
result = [context for context in contexts if context != default_context]
elif words[1] in ["--show", "-s"]:
# No filter for show
result = list(contexts)
return result

View File

@ -23,30 +23,33 @@
</header> </header>
<section id="section-intro"> <section id="section-intro">
<h2 id="connection-manager">Connection manager</h2> <h2 id="connection-manager">Connection manager</h2>
<p>Connpy is a connection manager that allows you to store nodes to connect them fast and password free.</p> <p>Connpy is a SSH, SFTP, Telnet, kubectl, and Docker pod connection manager and automation module for Linux, Mac, and Docker.</p>
<h3 id="features">Features</h3> <h3 id="features">Features</h3>
<pre><code>- You can generate profiles and reference them from nodes using @profilename so you dont <pre><code>- Manage connections using SSH, SFTP, Telnet, kubectl, and Docker exec.
need to edit multiple nodes when changing password or other information. - Set contexts to manage specific nodes from specific contexts (work/home/clients/etc).
- Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can - You can generate profiles and reference them from nodes using @profilename so you don't
be referenced using node@subfolder@folder or node@folder need to edit multiple nodes when changing passwords or other information.
- If you have too many nodes. Get completion script using: conn config --completion. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. They can
Or use fzf installing pyfzf and running conn config --fzf true be referenced using node@subfolder@folder or node@folder.
- Create in bulk, copy, move, export and import nodes for easy management. - If you have too many nodes, get a completion script using: conn config --completion.
- Run automation scripts in network devices. Or use fzf by installing pyfzf and running conn config --fzf true.
- use GPT AI to help you manage your devices. - Create in bulk, copy, move, export, and import nodes for easy management.
- Run automation scripts on network devices.
- Use GPT AI to help you manage your devices.
- Add plugins with your own scripts. - Add plugins with your own scripts.
- Much more! - Much more!
</code></pre> </code></pre>
<h3 id="usage">Usage</h3> <h3 id="usage">Usage</h3>
<pre><code>usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp] <pre><code>usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp]
conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config} ... conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config,sync,context} ...
positional arguments: positional arguments:
node|folder node[@subfolder][@folder] node|folder node[@subfolder][@folder]
Connect to specific node or show all matching nodes Connect to specific node or show all matching nodes
[@subfolder][@folder] [@subfolder][@folder]
Show all available connections globaly or in specified path Show all available connections globally or in specified path
Options:
options:
-h, --help show this help message and exit -h, --help show this help message and exit
-v, --version Show version -v, --version Show version
-a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder
@ -70,6 +73,7 @@ Commands:
plugin Manage plugins plugin Manage plugins
config Manage app config config Manage app config
sync Sync config with Google sync Sync config with Google
context Manage contexts with regex matching
</code></pre> </code></pre>
<h3 id="manage-profiles">Manage profiles</h3> <h3 id="manage-profiles">Manage profiles</h3>
<pre><code>usage: conn profile [-h] (--add | --del | --mod | --show) profile <pre><code>usage: conn profile [-h] (--add | --del | --mod | --show) profile
@ -86,14 +90,26 @@ options:
</code></pre> </code></pre>
<h3 id="examples">Examples</h3> <h3 id="examples">Examples</h3>
<pre><code> conn profile --add office-user <pre><code> #Add new profile
conn profile --add office-user
#Add new folder
conn --add @office conn --add @office
#Add new subfolder
conn --add @datacenter@office conn --add @datacenter@office
#Add node to subfolder
conn --add server@datacenter@office conn --add server@datacenter@office
#Add node to folder
conn --add pc@office conn --add pc@office
#Show node information
conn --show server@datacenter@office conn --show server@datacenter@office
#Connect to nodes
conn pc@office conn pc@office
conn server conn server
#Create and set new context
conn context -a office .*@office
conn context --set office
#Run a command in a node
conn run server ls -la
</code></pre> </code></pre>
<h2 id="plugin-requirements-for-connpy">Plugin Requirements for Connpy</h2> <h2 id="plugin-requirements-for-connpy">Plugin Requirements for Connpy</h2>
<h3 id="general-structure">General Structure</h3> <h3 id="general-structure">General Structure</h3>
@ -416,32 +432,35 @@ print(result)
&#39;&#39;&#39; &#39;&#39;&#39;
## Connection manager ## Connection manager
Connpy is a connection manager that allows you to store nodes to connect them fast and password free. Connpy is a SSH, SFTP, Telnet, kubectl, and Docker pod connection manager and automation module for Linux, Mac, and Docker.
### Features ### Features
- You can generate profiles and reference them from nodes using @profilename so you dont - Manage connections using SSH, SFTP, Telnet, kubectl, and Docker exec.
need to edit multiple nodes when changing password or other information. - Set contexts to manage specific nodes from specific contexts (work/home/clients/etc).
- Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can - You can generate profiles and reference them from nodes using @profilename so you don&#39;t
be referenced using node@subfolder@folder or node@folder need to edit multiple nodes when changing passwords or other information.
- If you have too many nodes. Get completion script using: conn config --completion. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. They can
Or use fzf installing pyfzf and running conn config --fzf true be referenced using node@subfolder@folder or node@folder.
- Create in bulk, copy, move, export and import nodes for easy management. - If you have too many nodes, get a completion script using: conn config --completion.
- Run automation scripts in network devices. Or use fzf by installing pyfzf and running conn config --fzf true.
- use GPT AI to help you manage your devices. - Create in bulk, copy, move, export, and import nodes for easy management.
- Run automation scripts on network devices.
- Use GPT AI to help you manage your devices.
- Add plugins with your own scripts. - Add plugins with your own scripts.
- Much more! - Much more!
### Usage ### Usage
``` ```
usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp] usage: conn [-h] [--add | --del | --mod | --show | --debug] [node|folder] [--sftp]
conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config} ... conn {profile,move,mv,copy,cp,list,ls,bulk,export,import,ai,run,api,plugin,config,sync,context} ...
positional arguments: positional arguments:
node|folder node[@subfolder][@folder] node|folder node[@subfolder][@folder]
Connect to specific node or show all matching nodes Connect to specific node or show all matching nodes
[@subfolder][@folder] [@subfolder][@folder]
Show all available connections globaly or in specified path Show all available connections globally or in specified path
Options:
options:
-h, --help show this help message and exit -h, --help show this help message and exit
-v, --version Show version -v, --version Show version
-a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder
@ -465,6 +484,7 @@ Commands:
plugin Manage plugins plugin Manage plugins
config Manage app config config Manage app config
sync Sync config with Google sync Sync config with Google
context Manage contexts with regex matching
``` ```
### Manage profiles ### Manage profiles
@ -485,14 +505,26 @@ options:
### Examples ### Examples
``` ```
#Add new profile
conn profile --add office-user conn profile --add office-user
#Add new folder
conn --add @office conn --add @office
#Add new subfolder
conn --add @datacenter@office conn --add @datacenter@office
#Add node to subfolder
conn --add server@datacenter@office conn --add server@datacenter@office
#Add node to folder
conn --add pc@office conn --add pc@office
#Show node information
conn --show server@datacenter@office conn --show server@datacenter@office
#Connect to nodes
conn pc@office conn pc@office
conn server conn server
#Create and set new context
conn context -a office .*@office
conn context --set office
#Run a command in a node
conn run server ls -la
``` ```
## Plugin Requirements for Connpy ## Plugin Requirements for Connpy
### General Structure ### General Structure
@ -2497,7 +2529,7 @@ def getitems(self, uniques):
- port (str): Port to connect to node, default 22 for ssh and 23 - port (str): Port to connect to node, default 22 for ssh and 23
for telnet. for telnet.
- protocol (str): Select ssh or telnet. Default is ssh. - protocol (str): Select ssh, telnet, kubectl or docker. Default is ssh.
- user (str): Username to of the node. - user (str): Username to of the node.
@ -2555,7 +2587,7 @@ class node:
- port (str): Port to connect to node, default 22 for ssh and 23 - port (str): Port to connect to node, default 22 for ssh and 23
for telnet. for telnet.
- protocol (str): Select ssh or telnet. Default is ssh. - protocol (str): Select ssh, telnet, kubectl or docker. Default is ssh.
- user (str): Username to of the node. - user (str): Username to of the node.
@ -2824,6 +2856,14 @@ class node:
connect = self._connect(timeout = timeout) connect = self._connect(timeout = timeout)
now = datetime.datetime.now().strftime(&#39;%Y-%m-%d_%H%M%S&#39;) now = datetime.datetime.now().strftime(&#39;%Y-%m-%d_%H%M%S&#39;)
if connect == True: if connect == True:
# Attempt to set the terminal size
try:
self.child.setwinsize(65535, 65535)
except Exception:
try:
self.child.setwinsize(10000, 10000)
except Exception:
pass
if &#34;prompt&#34; in self.tags: if &#34;prompt&#34; in self.tags:
prompt = self.tags[&#34;prompt&#34;] prompt = self.tags[&#34;prompt&#34;]
expects = [prompt, pexpect.EOF, pexpect.TIMEOUT] expects = [prompt, pexpect.EOF, pexpect.TIMEOUT]
@ -2911,6 +2951,14 @@ class node:
&#39;&#39;&#39; &#39;&#39;&#39;
connect = self._connect(timeout = timeout) connect = self._connect(timeout = timeout)
if connect == True: if connect == True:
# Attempt to set the terminal size
try:
self.child.setwinsize(65535, 65535)
except Exception:
try:
self.child.setwinsize(10000, 10000)
except Exception:
pass
if &#34;prompt&#34; in self.tags: if &#34;prompt&#34; in self.tags:
prompt = self.tags[&#34;prompt&#34;] prompt = self.tags[&#34;prompt&#34;]
expects = [prompt, pexpect.EOF, pexpect.TIMEOUT] expects = [prompt, pexpect.EOF, pexpect.TIMEOUT]
@ -2966,47 +3014,101 @@ class node:
return connect return connect
@MethodHook @MethodHook
def _connect(self, debug = False, timeout = 10, max_attempts = 3): def _generate_ssh_sftp_cmd(self):
# Method to connect to the node, it parse all the information, create the ssh/telnet command and login to the node.
if self.protocol in [&#34;ssh&#34;, &#34;sftp&#34;]:
cmd = self.protocol cmd = self.protocol
if self.idletime &gt; 0: if self.idletime &gt; 0:
cmd = cmd + &#34; -o ServerAliveInterval=&#34; + str(self.idletime) cmd += &#34; -o ServerAliveInterval=&#34; + str(self.idletime)
if self.port != &#39;&#39;: if self.port:
if self.protocol == &#34;ssh&#34;: if self.protocol == &#34;ssh&#34;:
cmd = cmd + &#34; -p &#34; + self.port cmd += &#34; -p &#34; + self.port
elif self.protocol == &#34;sftp&#34;: elif self.protocol == &#34;sftp&#34;:
cmd = cmd + &#34; -P &#34; + self.port cmd += &#34; -P &#34; + self.port
if self.options != &#39;&#39;: if self.options:
cmd = cmd + &#34; &#34; + self.options cmd += &#34; &#34; + self.options
if self.logs != &#39;&#39;: if self.jumphost:
self.logfile = self._logfile() cmd += &#34; &#34; + self.jumphost
if self.jumphost != &#39;&#39;: user_host = f&#34;{self.user}@{self.host}&#34; if self.user else self.host
cmd = cmd + &#34; &#34; + self.jumphost cmd += f&#34; {user_host}&#34;
if self.password[0] != &#39;&#39;: return cmd
passwords = self._passtx(self.password)
else: @MethodHook
passwords = [] def _generate_telnet_cmd(self):
if self.user == &#39;&#39;: cmd = f&#34;telnet {self.host}&#34;
cmd = cmd + &#34; {}&#34;.format(self.host) if self.port:
else: cmd += f&#34; {self.port}&#34;
cmd = cmd + &#34; {}&#34;.format(&#34;@&#34;.join([self.user,self.host])) if self.options:
expects = [&#39;yes/no&#39;, &#39;refused&#39;, &#39;supported&#39;, &#39;Invalid|[u|U]sage: (ssh|sftp)&#39;, &#39;ssh-keygen.*\&#34;&#39;, &#39;timeout|timed.out&#39;, &#39;unavailable&#39;, &#39;closed&#39;, &#39;[p|P]assword:|[u|U]sername:&#39;, r&#39;&gt;$|#$|\$$|&gt;.$|#.$|\$.$&#39;, &#39;suspend&#39;, pexpect.EOF, pexpect.TIMEOUT, &#34;No route to host&#34;, &#34;resolve hostname&#34;, &#34;no matching&#34;, &#34;[b|B]ad (owner|permissions)&#34;] cmd += f&#34; {self.options}&#34;
return cmd
@MethodHook
def _generate_kube_cmd(self):
cmd = f&#34;kubectl exec {self.options} {self.host} -it --&#34;
kube_command = self.tags.get(&#34;kube_command&#34;, &#34;/bin/bash&#34;) if isinstance(self.tags, dict) else &#34;/bin/bash&#34;
cmd += f&#34; {kube_command}&#34;
return cmd
@MethodHook
def _generate_docker_cmd(self):
cmd = f&#34;docker {self.options} exec -it {self.host}&#34;
docker_command = self.tags.get(&#34;docker_command&#34;, &#34;/bin/bash&#34;) if isinstance(self.tags, dict) else &#34;/bin/bash&#34;
cmd += f&#34; {docker_command}&#34;
return cmd
@MethodHook
def _get_cmd(self):
if self.protocol in [&#34;ssh&#34;, &#34;sftp&#34;]:
return self._generate_ssh_sftp_cmd()
elif self.protocol == &#34;telnet&#34;: elif self.protocol == &#34;telnet&#34;:
cmd = &#34;telnet &#34; + self.host return self._generate_telnet_cmd()
if self.port != &#39;&#39;: elif self.protocol == &#34;kubectl&#34;:
cmd = cmd + &#34; &#34; + self.port return self._generate_kube_cmd()
if self.options != &#39;&#39;: elif self.protocol == &#34;docker&#34;:
cmd = cmd + &#34; &#34; + self.options return self._generate_docker_cmd()
else:
raise ValueError(f&#34;Invalid protocol: {self.protocol}&#34;)
@MethodHook
def _connect(self, debug=False, timeout=10, max_attempts=3):
cmd = self._get_cmd()
passwords = self._passtx(self.password) if self.password[0] else []
if self.logs != &#39;&#39;: if self.logs != &#39;&#39;:
self.logfile = self._logfile() self.logfile = self._logfile()
if self.password[0] != &#39;&#39;: default_prompt = r&#39;&gt;$|#$|\$$|&gt;.$|#.$|\$.$&#39;
passwords = self._passtx(self.password) prompt = self.tags.get(&#34;prompt&#34;, default_prompt) if isinstance(self.tags, dict) else default_prompt
else: password_prompt = &#39;[p|P]assword:|[u|U]sername:&#39; if self.protocol != &#39;telnet&#39; else &#39;[p|P]assword:&#39;
passwords = []
expects = [&#39;[u|U]sername:&#39;, &#39;refused&#39;, &#39;supported&#39;, &#39;invalid option&#39;, &#39;ssh-keygen.*\&#34;&#39;, &#39;timeout|timed.out&#39;, &#39;unavailable&#39;, &#39;closed&#39;, &#39;[p|P]assword:&#39;, r&#39;&gt;$|#$|\$$|&gt;.$|#.$|\$.$&#39;, &#39;suspend&#39;, pexpect.EOF, pexpect.TIMEOUT, &#34;No route to host&#34;, &#34;resolve hostname&#34;, &#34;no matching&#34;, &#34;[b|B]ad (owner|permissions)&#34;] expects = {
else: &#34;ssh&#34;: [&#39;yes/no&#39;, &#39;refused&#39;, &#39;supported&#39;, &#39;Invalid|[u|U]sage: ssh&#39;, &#39;ssh-keygen.*\&#34;&#39;, &#39;timeout|timed.out&#39;, &#39;unavailable&#39;, &#39;closed&#39;, password_prompt, prompt, &#39;suspend&#39;, pexpect.EOF, pexpect.TIMEOUT, &#34;No route to host&#34;, &#34;resolve hostname&#34;, &#34;no matching&#34;, &#34;[b|B]ad (owner|permissions)&#34;],
raise ValueError(&#34;Invalid protocol: &#34; + self.protocol) &#34;sftp&#34;: [&#39;yes/no&#39;, &#39;refused&#39;, &#39;supported&#39;, &#39;Invalid|[u|U]sage: sftp&#39;, &#39;ssh-keygen.*\&#34;&#39;, &#39;timeout|timed.out&#39;, &#39;unavailable&#39;, &#39;closed&#39;, password_prompt, prompt, &#39;suspend&#39;, pexpect.EOF, pexpect.TIMEOUT, &#34;No route to host&#34;, &#34;resolve hostname&#34;, &#34;no matching&#34;, &#34;[b|B]ad (owner|permissions)&#34;],
&#34;telnet&#34;: [&#39;[u|U]sername:&#39;, &#39;refused&#39;, &#39;supported&#39;, &#39;invalid|unrecognized option&#39;, &#39;ssh-keygen.*\&#34;&#39;, &#39;timeout|timed.out&#39;, &#39;unavailable&#39;, &#39;closed&#39;, password_prompt, prompt, &#39;suspend&#39;, pexpect.EOF, pexpect.TIMEOUT, &#34;No route to host&#34;, &#34;resolve hostname&#34;, &#34;no matching&#34;, &#34;[b|B]ad (owner|permissions)&#34;],
&#34;kubectl&#34;: [&#39;[u|U]sername:&#39;, &#39;[r|R]efused&#39;, &#39;[E|e]rror&#39;, &#39;DEPRECATED&#39;, pexpect.TIMEOUT, password_prompt, prompt, pexpect.EOF, &#34;expired|invalid&#34;],
&#34;docker&#34;: [&#39;[u|U]sername:&#39;, &#39;Cannot&#39;, &#39;[E|e]rror&#39;, &#39;failed&#39;, &#39;not a docker command&#39;, &#39;unknown&#39;, &#39;unable to resolve&#39;, pexpect.TIMEOUT, password_prompt, prompt, pexpect.EOF]
}
error_indices = {
&#34;ssh&#34;: [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16],
&#34;sftp&#34;: [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16],
&#34;telnet&#34;: [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16],
&#34;kubectl&#34;: [1, 2, 3, 4, 8], # Define error indices for kube
&#34;docker&#34;: [1, 2, 3, 4, 5, 6, 7] # Define error indices for docker
}
eof_indices = {
&#34;ssh&#34;: [8, 9, 10, 11],
&#34;sftp&#34;: [8, 9, 10, 11],
&#34;telnet&#34;: [8, 9, 10, 11],
&#34;kubectl&#34;: [5, 6, 7], # Define eof indices for kube
&#34;docker&#34;: [8, 9, 10] # Define eof indices for docker
}
initial_indices = {
&#34;ssh&#34;: [0],
&#34;sftp&#34;: [0],
&#34;telnet&#34;: [0],
&#34;kubectl&#34;: [0], # Define special indices for kube
&#34;docker&#34;: [0] # Define special indices for docker
}
attempts = 1 attempts = 1
while attempts &lt;= max_attempts: while attempts &lt;= max_attempts:
child = pexpect.spawn(cmd) child = pexpect.spawn(cmd)
@ -3014,54 +3116,55 @@ class node:
print(cmd) print(cmd)
self.mylog = io.BytesIO() self.mylog = io.BytesIO()
child.logfile_read = self.mylog child.logfile_read = self.mylog
if len(passwords) &gt; 0:
loops = len(passwords)
else:
loops = 1
endloop = False endloop = False
for i in range(0, loops): for i in range(len(passwords) if passwords else 1):
while True: while True:
results = child.expect(expects, timeout=timeout) results = child.expect(expects[self.protocol], timeout=timeout)
if results == 0: results_value = expects[self.protocol][results]
if results in initial_indices[self.protocol]:
if self.protocol in [&#34;ssh&#34;, &#34;sftp&#34;]: if self.protocol in [&#34;ssh&#34;, &#34;sftp&#34;]:
child.sendline(&#39;yes&#39;) child.sendline(&#39;yes&#39;)
elif self.protocol == &#34;telnet&#34;: elif self.protocol in [&#34;telnet&#34;, &#34;kubectl&#34;]:
if self.user != &#39;&#39;: if self.user:
child.sendline(self.user) child.sendline(self.user)
else: else:
self.missingtext = True self.missingtext = True
break break
if results in [1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16]:
elif results in error_indices[self.protocol]:
child.terminate() child.terminate()
if results == 12 and attempts != max_attempts: if results_value == pexpect.TIMEOUT and attempts != max_attempts:
attempts += 1 attempts += 1
endloop = True endloop = True
break break
else: else:
if results == 12: after = &#34;Connection timeout&#34; if results_value == pexpect.TIMEOUT else child.after.decode()
after = &#34;Connection timeout&#34; return f&#34;Connection failed code: {results}\n{child.before.decode().lstrip()}{after}{child.readline().decode()}&#34;.rstrip()
else:
after = child.after.decode() elif results in eof_indices[self.protocol]:
return (&#34;Connection failed code:&#34; + str(results) + &#34;\n&#34; + child.before.decode().lstrip() + after + child.readline().decode()).rstrip() if results_value == password_prompt:
if results == 8: if passwords:
if len(passwords) &gt; 0:
child.sendline(passwords[i]) child.sendline(passwords[i])
else: else:
self.missingtext = True self.missingtext = True
break break
if results in [9, 11]: elif results_value == &#34;suspend&#34;:
child.sendline(&#34;\r&#34;)
sleep(2)
else:
endloop = True endloop = True
child.sendline() child.sendline()
break break
if results == 10:
child.sendline(&#34;\r&#34;)
sleep(2)
if endloop: if endloop:
break break
if results == 12: if results_value == pexpect.TIMEOUT:
continue continue
else: else:
break break
child.readline(0) child.readline(0)
self.child = child self.child = child
return True</code></pre> return True</code></pre>
@ -3208,6 +3311,14 @@ def run(self, commands, vars = None,*, folder = &#39;&#39;, prompt = r&#39;&gt;$
connect = self._connect(timeout = timeout) connect = self._connect(timeout = timeout)
now = datetime.datetime.now().strftime(&#39;%Y-%m-%d_%H%M%S&#39;) now = datetime.datetime.now().strftime(&#39;%Y-%m-%d_%H%M%S&#39;)
if connect == True: if connect == True:
# Attempt to set the terminal size
try:
self.child.setwinsize(65535, 65535)
except Exception:
try:
self.child.setwinsize(10000, 10000)
except Exception:
pass
if &#34;prompt&#34; in self.tags: if &#34;prompt&#34; in self.tags:
prompt = self.tags[&#34;prompt&#34;] prompt = self.tags[&#34;prompt&#34;]
expects = [prompt, pexpect.EOF, pexpect.TIMEOUT] expects = [prompt, pexpect.EOF, pexpect.TIMEOUT]
@ -3336,6 +3447,14 @@ def test(self, commands, expected, vars = None,*, prompt = r&#39;&gt;$|#$|\$$|&g
&#39;&#39;&#39; &#39;&#39;&#39;
connect = self._connect(timeout = timeout) connect = self._connect(timeout = timeout)
if connect == True: if connect == True:
# Attempt to set the terminal size
try:
self.child.setwinsize(65535, 65535)
except Exception:
try:
self.child.setwinsize(10000, 10000)
except Exception:
pass
if &#34;prompt&#34; in self.tags: if &#34;prompt&#34; in self.tags:
prompt = self.tags[&#34;prompt&#34;] prompt = self.tags[&#34;prompt&#34;]
expects = [prompt, pexpect.EOF, pexpect.TIMEOUT] expects = [prompt, pexpect.EOF, pexpect.TIMEOUT]

View File

@ -1,10 +1,11 @@
Flask>=2.3.2 Flask>=2.3.2
Flask_Cors>=4.0.1
google_api_python_client>=2.125.0 google_api_python_client>=2.125.0
google_auth_oauthlib>=1.2.0 google_auth_oauthlib>=1.2.0
inquirer>=3.2.4 inquirer>=3.3.0
openai>=0.27.8 openai>=0.27.8
pexpect>=4.8.0 pexpect>=4.8.0
protobuf>=5.26.1 protobuf>=5.27.2
pycryptodome>=3.18.0 pycryptodome>=3.18.0
pyfzf>=0.3.1 pyfzf>=0.3.1
PyYAML>=6.0.1 PyYAML>=6.0.1

View File

@ -29,6 +29,7 @@ install_requires =
pexpect pexpect
pycryptodome pycryptodome
Flask Flask
Flask_Cors
pyfzf pyfzf
waitress waitress
PyYAML PyYAML