-
Notifications
You must be signed in to change notification settings - Fork 20
/
passhport
executable file
·283 lines (230 loc) · 10.6 KB
/
passhport
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#!/usr/bin/env python
# -*- coding:Utf-8 -*-
# Compatibility 2.7-3.4
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import input
from datetime import datetime
from tabulate import tabulate
import os, sys, locale, requests, random, crypt, re, configparser, signal
from connections_utils import ssh
from connections_utils import scp
from connections_utils import password
# Reading configuration from /etc if possible else form the script directory
conf = configparser.ConfigParser()
conffile = "passhport.ini"
if os.path.isfile("/etc/passhport/" + conffile):
conf.read("/etc/passhport/" + conffile)
else:
conf.read(sys.path[0] + "/" + conffile)
# SSL Configuration
SSL = conf.getboolean("SSL", "SSL")
SSL_CERTIFICAT = conf.get("SSL", "SSL_CERTIFICAT")
SCRIPT_LOGS_PATH = conf.get("Environment", "SCRIPT_LOGS_PATH")
PWD_FILE_DIR = conf.get("RootPasswordRest", "PWD_FILE_DIR")
PWD_RST_AFTER_SESSION = conf.getboolean("RootPasswordRest", \
"PWD_RST_AFTER_SESSION")
def signal_handler(signal, frame):
"""Handle 'ctrl-c' from the users"""
print("\nExiting at your own sweet will.")
sys.exit(0)
def get(url):
"""Send the GET request to the server and print a result.
This is used to connect to passhportd, to check access"""
try:
if SSL:
r = requests.get(url, verify=SSL_CERTIFICAT)
else:
r = requests.get(url)
except requests.RequestException as e:
print("ERROR: " + str(e.message))
else:
if r.status_code == requests.codes.ok:
return r.text
return 1
def post(url, data):
"""Send the POST request to the server and print a result.
This is used to connect to passhportd, to log access"""
try:
if SSL:
r = requests.post(url_passhport + url, data=data,
verify=SSL_CERTIFICAT)
else:
r = requests.post(url_passhport + url, data=data)
except requests.RequestException as e:
print("ERROR: " + str(e.message))
else:
if r.status_code == requests.codes.ok:
return r.text
return 1
def checkandconnect(indexed_target_list, choice, username, originalcmd,
scpcmd = False):
"""Check if the user have entered an accessible target
then check target type and launch the action"""
#In order to allow an automatic login name for each user:
login = re.split("@", username)[0] #We take the first part of the email
# Check the target access and access type
for line in indexed_target_list:
item_list = line.split(" ")
for possibility in item_list:
if choice == possibility.casefold():
if len(item_list) > 2:
date = datetime.today().strftime('%Y/%m/%d')
targetname = item_list[1]
target = item_list[2]
login = get(url_passhport + "target" + "/login/" + \
targetname)
# If target login is set at $email =>
# we user login=username (without @...)
if login == "$email":
login = re.split("@", username)[0]
pathlog = SCRIPT_LOGS_PATH + "/" + date + "/"
# Create a log directory for this target
if not os.path.isdir(pathlog):
os.makedirs(pathlog)
isodate = datetime.now().isoformat().replace(":",""). \
replace("-","").split('.')[0]
filename = isodate + "-PID" + \
str(os.getpid()) + "-" + targetname + "-" + \
login + "@" + target + "-" + username
filelog = pathlog + filename
port = get(url_passhport + "target" + "/port/" +
targetname)
sshoptions = get(url_passhport + "target" + \
"/sshoptions/" + targetname)
if scpcmd == False:
cmd = "ssh"
else:
cmd = originalcmd
data= {"connexiondate" : isodate,
"connexioncmd" : cmd,
"logfilepath" : pathlog,
"logfilename" : filename,
"user" : username,
"target" : targetname}
# Log the entry on the database
r = post("logentry/create", data)
# It's SCP if scpcmd is set, else ssh.
if scpcmd == True:
scp.connect(target, filelog, login, sshoptions,
originalcmd)
else:
ssh.connect(target, filelog, login, port, sshoptions,
originalcmd)
# Change password on the target
if PWD_RST_AFTER_SESSION:
password.reset(target, login, sshoptions, port, PWD_FILE_DIR)
return True
else:
print("Error on target definition")
return False
def build_targetnames_list(target_list):
"""Return a list with targets names only"""
targetnames_list = []
for each_target in target_list:
targetnames_list.append(each_target.split(" ", 1)[0]) # target name
return targetnames_list
def build_targets_list(target_list):
""" Return a list with targets names, hostnames and a number"""
""" Globaly the list without comments """
indexed_target_list = []
number = 1
for each_target in target_list:
indexed_target_list.append(str(number) + " " +
each_target.split(" ", 1)[0] + " " +
" ".join(each_target.split(" ")[1:2]))
number = number + 1
return indexed_target_list
def build_commented_targets_list(target_list, tabulations=0):
""" Build a targets list in a readable and numbered list format"""
indexed_target_list = []
table = []
number = 1
for each_target in target_list:
if not tabulations:
indexed_target_list.append(str(number) + " " + each_target)
else:
line=[]
line.append(str(number))
line.append(each_target.split(" ", 1)[0]) # target name
line.append(" ".join(each_target.split(" ")[1:2])) # hostname
line.append(" ".join(each_target.split(" ")[2:])) # comment
table.append(line)
number = number + 1
if tabulations:
indexed_target_list = tabulate(table, tablefmt="plain")
return indexed_target_list
def direct_connection(username, url_passhport, target_list, originalcmd):
""" Handle SCP connections and SSH direct commands connections """
if ( re.sub("^scp ", "", originalcmd) == originalcmd ):
# SSH direct command
ssh_direct_command(username, url_passhport, target_list, originalcmd)
else:
scp_command(username, url_passhport, target_list, originalcmd)
def ssh_direct_command(username, url_passhport, target_list, originalcmd):
""" Prepare and launch a direct ssh command/connection """
splitcommand = originalcmd.split(" ")
server = splitcommand[0]
# Check rights access
if server not in build_targetnames_list(target_list):
sys.exit("Error: This server is not in your access list")
if len(splitcommand) < 2:
# if there is no command, we connect as normal ssh connection
checkandconnect(build_targets_list(target_list), server, username,
"")
else:
# else we remove the target name
checkandconnect(build_targets_list(target_list), server, username,
re.sub("^" + server + " ", "", originalcmd))
def scp_command(username, url_passhport, target_list, originalcmd):
""" Prepare and launch the scp """
parsedcommand = scp.parse(originalcmd)
if len(parsedcommand) > 1:
choice = parsedcommand[0] # the target
command = parsedcommand[1] # the rewrited command
# Setting scpcmd flag at true
checkandconnect(indexed_target_list, choice, username, command, True)
else:
sys.exit("Syntax error on the scp command.")
### MAIN ###
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
username = sys.argv[1]
url_passhport = "http" + conf.getboolean("SSL", "SSL")*"s" + \
"://" + conf.get("Network", "PASSHPORTD_HOSTNAME") + \
":" + conf.get("Network", "PORT") + "/"
try:
target_list = get(url_passhport + "user" + "/accessible_targets/" + \
username).split("\n")
except AttributeError as e:
sys.exit("No user exists in database or can't reach passhportd.\n" +
"tip: it can be a SSL certificate misconfiguration.")
originalcmd = os.environ.get('SSH_ORIGINAL_COMMAND')
choice = 0
# If the user can't access any target
if target_list == [u'']:
sys.exit("Sorry you can't access any server")
#Construct the indexted targets list
indexed_target_list = build_targets_list(target_list)
# Check if it's interactive ssh or another type of connection:
if originalcmd:
# In case the SSH_ORIGINAL_COMMAND is set,
# it should be scp/rsync (according to sshd manpage)
# After testing, it can be ssh with a direct command too...
direct_connection(username, url_passhport, target_list, originalcmd)
else:
# We handle the interactive ssh connection
print("Welcome {}.".format(username))
print("Here is the list of servers you can access:")
# Print servers the user can access,
# with a blank line between each entry
print(build_commented_targets_list(target_list, tabulations=1))
# Let the use chose his server
sys.stdout.write("Type the number, name or hostname of the server you want to connect to : ")
choice = input("").casefold()
valid_input = False
# Try to connect the user to the server he choosed or ask to re-type
while not checkandconnect(indexed_target_list, choice, username, originalcmd):
print("You didn't type correctly, please try again.")
sys.stdout.write("Type the number, name or hostname of the server you want to connect to : ")
choice = input("").casefold()