-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathissh.py
155 lines (129 loc) · 5.43 KB
/
issh.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import curses
import curses.ascii
from os import system, environ
from os.path import join, expanduser, exists
import sys
from signal import signal, SIGINT, SIGTERM
class ISSH:
def __init__(self, screen):
print('[*] Initializing ISSH')
signal(SIGINT, self.shutdown)
signal(SIGTERM, self.shutdown)
self.hosts = list()
self.ssh_config_path = join(expanduser("~"), ".ssh", "config")
self.check_if_ssh_config_exists()
self.load_ssh_hosts()
self.active_choice = 0
self.screen = screen
self.screen.keypad(1)
curses.curs_set(0)
curses.noecho()
curses.start_color()
curses.init_pair(1, curses.COLOR_CYAN, curses.COLOR_BLACK)
def check_if_ssh_config_exists(self):
if not exists(self.ssh_config_path):
curses.endwin()
print('No SSH config file detected at ' + self.ssh_config_path + '. Aborting.')
sys.exit(1)
def load_ssh_hosts(self):
self.hosts = list()
with open(self.ssh_config_path) as ssh_config:
for line in ssh_config.readlines():
line = line.rstrip()
if len(line) == 0 or line[0] == ' ' or line[0] == '\t' or line.lstrip()[0] == '#' or line.find("*") > -1:
continue
try:
self.hosts.append(line.split()[1])
except IndexError:
print(f"[-] Warning: Invalid host format detected on line: {line}")
self.hosts.sort()
def run(self):
self.input_loop()
def print_options(self):
self.screen.clear()
num_header_rows = 2
self.screen.addstr(0, 0, "Select an SSH host (Press H for help):")
for i, host in enumerate(self.hosts[self.active_choice:]):
try:
if i == 0:
self.screen.addstr(i + num_header_rows, 0, f" > {host}", curses.color_pair(1) | curses.A_BOLD)
else:
self.screen.addstr(i + num_header_rows, 0, f" {host}")
except:
# Suppress error if list is longer than window
pass
self.screen.refresh()
def input_loop(self):
while True:
self.print_options()
try:
char_ord = self.screen.getch()
char = chr(char_ord)
if char.upper() == 'Q' or char_ord == curses.ascii.ESC: # Esc or Q
self.shutdown()
elif char.upper() == 'J' or char_ord == curses.KEY_DOWN: # Down or J
if self.active_choice < len(self.hosts) - 1:
self.active_choice += 1
elif char.upper() == 'K' or char_ord == curses.KEY_UP: # Up or K
if self.active_choice > 0:
self.active_choice -= 1
elif char == 'g': # Move to top
self.active_choice = 0
elif char == 'G': # Move to last item
self.active_choice = len(self.hosts) - 1
elif char.upper() == 'E':
self.launch_editor()
elif char.upper() == 'R': # Refresh screen
self.load_ssh_hosts()
elif char.upper() == 'H': # Print help screen
self.print_help_screen()
elif char_ord == curses.ascii.LF or char.upper() == 'L' or char_ord == curses.KEY_RIGHT: # Enter, L or Right
# Choice has been selected already, exit the menu system
break
except Exception as e:
print('[-] Invalid keypress detected.')
print(e)
# After breaking out of loop, ssh to the active target
self.cleanup_curses()
system(f"ssh {self.hosts[self.active_choice]}")
def cleanup_curses(self):
self.screen.keypad(0)
curses.curs_set(1)
curses.echo()
curses.endwin()
def shutdown(self):
self.cleanup_curses()
sys.exit(0)
def launch_editor(self):
editor = environ.get('EDITOR')
if editor is None: # Default editors
if sys.platform == 'win32':
editor = 'notepad.exe'
elif sys.platform == 'darwin':
editor = 'nano'
elif 'linux' in sys.platform:
editor = 'vi'
system(f"{editor} {self.ssh_config_path}")
self.load_ssh_hosts() # Reload hosts after changes
def print_help_screen(self):
self.screen.clear()
self.screen.addstr(0, 0, "Help information:")
self.screen.addstr(2, 0, " H - This help screen")
self.screen.addstr(3, 0, " Q or ESC - Quit the program")
self.screen.addstr(4, 0, " E - Edit SSH config file")
self.screen.addstr(5, 0, " R - Reload SSH hosts from config file")
self.screen.addstr(6, 0, " Down or J - Move selection down")
self.screen.addstr(7, 0, " Up or K - Move selection up")
self.screen.addstr(8, 0, " Right or L or Enter - SSH to current selection")
self.screen.addstr(9, 0, " G - Move to last item")
self.screen.addstr(10, 0, " g - Move to first item")
self.screen.addstr(12, 0, "Press any key to continue")
# Wait for any key press
self.screen.getch()
def main_wrapper(main_screen):
issh = ISSH(main_screen)
issh.run()
def main():
curses.wrapper(main_wrapper)
if __name__ == '__main__':
main()