This commit is contained in:
QunL 2018-07-12 19:37:29 +00:00 committed by GitHub
commit ae14c74b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 400 additions and 1 deletions

View File

@ -86,6 +86,9 @@ and websockify as a browser telnet client (`wstelnet.html`).
These are not necessary for the basic operation.
* Muliti-vncserver: Use path in URL to pass VNC server address:port for
connecting different VNC servers.
* Daemonizing: When the `-D` option is specified, websockify runs
in the background as a daemon process.

150
websockify/encode_url.py Normal file
View File

@ -0,0 +1,150 @@
#!/usr/bin/env python
'''The program encode and decode the string with base64 and hash to validate the integrality.
'''
import sys
import hashlib
import base64
import datetime
import group
# Please change the salt for your own project.
#SALT = "Some salt for security. liqun@ncl.sg"
SALT = "Some salt for security. Please change it in your project. liqun@ncl.sg"
def qencode(str, create_date=datetime.date.today(), salt=SALT):
''' The func encode str, hash it and base64 it.'''
alg = hashlib.sha256()
tstr = create_date.strftime("%Y%m%d")
alg.update(tstr)
alg.update(':')
alg.update(str)
alg.update(salt)
hash = alg.hexdigest()
return base64.urlsafe_b64encode(hash+tstr+':'+str)
def qdecode(str, valid_daynum=3, salt=SALT):
''' The func decode str, validate with hash and base64.decode it.
If valid_daynum =0, only today is valid.'''
if str[-1] == '/':
str1 = base64.urlsafe_b64decode(str[0:-1])
else:
str1 = base64.urlsafe_b64decode(str)
pos = str1.find(':')
if pos == -1:
return '','Err:not find :'
hash = str1[0:pos-8]
tstr = str1[pos-8:pos]
url_date = datetime.date(int(str1[pos-8:pos-4]), int(str1[pos-4:pos-2]), int(str1[pos-2:pos]))
today = datetime.date.today()
if (today - url_date) > datetime.timedelta(valid_daynum):
return '', 'Err: Timeout'
alg = hashlib.sha256()
alg.update(str1[pos-8:])
alg.update(salt)
hash1 = alg.hexdigest()
if hash != hash1:
return '', 'Err: Wrong hash'
return str1[pos+1:], ''
def get_server_from_path(path, is_encoded, valid_daynum=3, salt=SALT, gpolicy=group.gpolicy):
'''The func decode host port from path parameter.
path looks like [/qencode(n1.soc.cloud.ncl.sg:5901)]
'''
phost = ''
if path[-1] == '/':
path = path[0:-1]
try:
pos = path.rfind('/')
if pos == -1:
return '', 0
if is_encoded:
(str,err) = qdecode(path[pos+1:], valid_daynum, salt)
if err != '':
phost = err
else:
str = path[pos+1:]
if str == '':
return phost, 0
part_list = str.split(':')
phost = part_list[0]
pport = int(part_list[1])
if len(part_list) > 2:
username = part_list[2]
# check the access ability.
if not group.can_access(username, phost, gpolicy):
return 'Err: group policy block', 0
except:
raise
#return phost, 0
return phost, pport
def test_basic():
'''The func test some basic func.'''
assert get_server_from_path('/n1.soc.cloud.ncl.sg:5901', False) == ('n1.soc.cloud.ncl.sg', 5901)
str = "n1.soc.cloud.ncl.sg:5901"
enc = qencode(str)
print enc
print base64.urlsafe_b64decode(enc)
(dec,err) = qdecode(enc)
assert str == dec
assert get_server_from_path('/'+enc, True) == ('n1.soc.cloud.ncl.sg', 5901)
enc = qencode(str, datetime.date(2018, 2, 24))
assert (get_server_from_path('/'+enc, True)) == ('Err: Timeout', 0)
enc = qencode(str, datetime.date.today(), 'test salt')
(dec,err) = qdecode(enc, 3, 'test salt')
assert str == dec
(dec,err) = qdecode(enc, 3, 'test salt1')
assert dec == ''
str = "n1.soc.cloud.ncl.sg:5901:ntechni3"
enc = qencode(str)
print enc
print base64.urlsafe_b64decode(enc)
(dec,err) = qdecode(enc)
assert str == dec
assert get_server_from_path('/'+enc, True) == ('n1.soc.cloud.ncl.sg', 5901)
(dec,err) = qdecode(enc + '/')
assert str == dec
gpolicy = {
"ExperimentDomainName":"soc.cloud.ncl.sg",
'Groups':[{
'Name':'Red',
'Users': ["user1", "ntechni3"],
'Hosts': ["n1"]
},
{
'Name':'Blue',
'Users': ['user1'],
'Hosts': ['n2','n3']
}]
}
str = "n1.soc.cloud.ncl.sg:5901:ntechni3"
enc = qencode(str)
assert get_server_from_path('/'+enc, True, 3, SALT, gpolicy) == ('n1.soc.cloud.ncl.sg', 5901)
str = "n2.soc.cloud.ncl.sg:5901:ntechni3"
enc = qencode(str)
assert get_server_from_path('/'+enc, True, 3, SALT, gpolicy) == ('Err: group policy block', 0)
def main():
'''The func is the main func.'''
import sys
if (len(sys.argv) == 2) and (sys.argv[1] == "test"):
test_basic()
print "Pass all test"
exit()
elif (len(sys.argv) == 2):
print qencode(sys.argv[1])
elif (len(sys.argv) == 3) and (sys.argv[1] == "decode"):
print get_server_from_path(sys.argv[2], True)
#print qdecode(sys.argv[2])
elif (len(sys.argv) == 5) and (sys.argv[1] == "decode"):
print qdecode(sys.argv[2],int(sys.argv[3]),sys.argv[4])
elif (len(sys.argv) == 5) and (sys.argv[1] == "encode"):
print qencode(sys.argv[2],int(sys.argv[3]),sys.argv[4])
elif (len(sys.argv) == 3) and (sys.argv[1] == "debase"):
str1 = base64.urlsafe_b64decode(sys.argv[2])
print str1
else:
print '''%s [content to encode] [valid day number:3] [salt to encrypt]
decode [content to decode] [valid day number:3] [salt to encrypt]
test''' % sys.argv[0]
if __name__ == "__main__":
main()

221
websockify/group.py Normal file
View File

@ -0,0 +1,221 @@
#!/usr/bin/env python
import subprocess
gpolicy = { }
def group(users, hosts, ousers, ohosts):
'''The func handle group policy for one group. Users can access hosts.'''
pass
def get_users_hosts(gpolicy):
'''The func Get the all user list, hosts, hosts_access'''
users = set()
hosts = {}
hosts_access = {}
# Get the all user list, hosts, hosts_access
for group in gpolicy['Groups']:
if not group.has_key('Name'):
print "The group do not have key [Name]"
continue;
for user in group['Users']:
users.add(user)
if not hosts_access.has_key(user):
hosts_access[user] = set()
if group.has_key('Hosts'):
hosts_access[user] = hosts_access[user].union(group['Hosts'])
# get all host list.
if group.has_key('Hosts'):
for host in group['Hosts']:
hosts[host] = ''
return users, hosts, hosts_access
def get_super_users(gpolicy, users, hosts_access):
'''The func Get all super user list. User in super user list need be
deleted in user list and hosts_access.'''
super_users = set()
# Get all super user list.
for group in gpolicy['Groups']:
if not group.has_key('Name'):
print "The group do not have key [Name]"
continue;
if not group.has_key('Hosts'):
for user in group['Users']:
# Delete super user from normal user list.
if user in users:
users.remove(user)
del hosts_access[user]
super_users.add(user)
return (super_users, users, hosts_access)
def get_unaccess_hosts(hosts_access, hosts):
hosts_unaccess = {}
for user in hosts_access.keys():
hosts_unaccess[user] = set()
for host in hosts:
if host not in hosts_access[user]:
hosts_unaccess[user].add(host)
return hosts_unaccess
def can_access(username, node_url, gpolicy):
'''The func return true or False, based on gpolicy allowing username to node_url or not.'''
if not gpolicy.has_key('ExperimentDomainName'):
print 'Err: no ExperimentDomainName'
return True
ename = gpolicy['ExperimentDomainName']
exp_name = ename
dnode = node_url[:node_url.find('.')]
dexp = node_url[node_url.find('.')+1:]
if dexp != exp_name :
# not the experience group return true.
return True
for group in gpolicy['Groups']:
find_host = False
find_user = False
if not group.has_key('Name'):
print "The group do not have key [Name]"
continue;
if group.has_key('Hosts'):
for host in group['Hosts']:
if host == dnode:
find_host = True
else:
#super user group, can access all nodes.
find_host = True
if group.has_key('Users'):
for user in group['Users']:
if username == user:
find_user = True
if find_host and find_user :
return True
return False
def group_exp(gpolicy):
ename = gpolicy['ExperimentDomainName']
users = ()
super_users = ()
hosts = {}
hosts_access = {}
hosts_unaccess = {}
# Get the all user list, hosts, hosts_access
(users, hosts, hosts_access) = get_users_hosts(gpolicy)
# Get all super user list.
(super_users, users, hosts_access) = get_super_users(gpolicy, users, hosts_access)
# produce unaccess hosts for every user.
hosts_unaccess = get_unaccess_hosts(hosts_access, hosts)
# For every normal user, get all accessable host list and give access right, other give no right.
for user in users:
for host in hosts_access[user]:
hosts[host] += 'sudo usermod -e "" %s\n' % user
for user in users:
for host in hosts_unaccess[user]:
hosts[host] += 'sudo usermod -e 1 %s\n' % user
# For every super user, give all access.
for user in super_users:
for host in hosts.keys():
hosts[host] += 'sudo usermod -e "" %s\n' % user
return hosts
def test():
gpolicy = {
"ExperimentDomainName":"EnterpriseNetwork.NYPSOC.ncl.sg",
'Groups':[]
}
assert get_users_hosts(gpolicy) == (set([]), {}, {})
gpolicy = {
"ExperimentDomainName":"EnterpriseNetwork.NYPSOC.ncl.sg",
'Groups':[{
'Name':'Red',
'Users': ["user1", "user2"],
'Hosts': ["n1"]
},
{
'Name':'Blue',
'Users': ['user3'],
'Hosts': ['n2','n3']
},
{
'Name':'Grey',
'Users': ['user4'],
'Hosts': []
},
{
'Name':'Super',
'Users': ['user3']
}]
}
(users, hosts, hosts_access) = get_users_hosts(gpolicy)
assert (users, hosts, hosts_access) == (set(['user4', 'user2', 'user3', 'user1']),\
{'n1': '', 'n2': '', 'n3': ''}, \
{'user4': set([]), 'user2': set(['n1']), 'user3': set(['n2', 'n3']), 'user1': set(['n1'])})
assert get_unaccess_hosts(hosts_access, hosts) == {
'user4': set(['n1', 'n2', 'n3']), 'user2': set(['n2', 'n3']),
'user3': set(['n1']), 'user1': set(['n2', 'n3'])}
(super_users, users, hosts_access) = get_super_users(gpolicy, users, hosts_access)
assert (super_users, users, hosts_access) == (set(['user3']),
set(['user4', 'user2', 'user1']), \
{'user4': set([]), 'user2': set(['n1']), 'user1': set(['n1'])})
assert get_unaccess_hosts(hosts_access, hosts) == {
'user4': set(['n1', 'n2', 'n3']), 'user2': set(['n2', 'n3']),
'user1': set(['n2', 'n3'])}
print group_exp(gpolicy)
group_exp(gpolicy) == {'n1': 'sudo usermod -e "" user2\nsudo usermod -e "" user1\nsudo usermod -e 1 user4\nsudo usermod -e "" user3\n', 'n2': 'sudo usermod -e 1 user4\nsudo usermod -e 1 user2\nsudo usermod -e 1 user1\nsudo usermod -e "" user3\n', 'n3': 'sudo usermod -e 1 user4\nsudo usermod -e 1 user2\nsudo usermod -e 1 user1\nsudo usermod -e "" user3\n'}
assert get_unaccess_hosts({'user1': set(['n1'])}, {'n1': ''}) == {'user1': set([])}
assert get_unaccess_hosts({'user1': set([])}, {'n1': ''}) == {'user1': set(['n1'])}
assert get_unaccess_hosts({'user1': set(['n1', 'n2'])}, {'n1': '', 'n2': ''}) == {'user1': set([])}
gpolicy = {
"ExperimentDomainName":"EnterpriseNetwork.NYPSOC.ncl.sg",
'Groups':[{
'Name':'Red',
'Users': ["user1", "user2"],
'Hosts': ["n1"]
},
{
'Name':'Blue',
'Users': ['user1'],
'Hosts': ['n2','n3']
} ]
}
(users, hosts, hosts_access) = get_users_hosts(gpolicy)
#print (users, hosts, hosts_access)
assert (users, hosts, hosts_access) == (set(['user2', 'user1']), \
{'n1': '', 'n2': '', 'n3': ''}, {'user2': set(['n1']), 'user1': set(['n1', 'n2', 'n3'])})
gpolicy = {
"ExperimentDomainName":"EnterpriseNetwork.NYPSOC.ncl.sg",
'Groups':[{
'Name':'Red',
'Users': ["user1", "user2"],
'Hosts': ["n1"]
},
{
'Name':'Blue',
'Users': ['user1'],
'Hosts': ['n2','n3']
},
{ 'Name':'superusers', 'Users':['suser']}
]
}
assert can_access('user1', 'n1.EnterpriseNetwork.NYPSOC.ncl.sg', gpolicy) == True
assert can_access('user1', 'n1.another.NYPSOC.ncl.sg', gpolicy) == True
assert can_access('user1', 'n2.EnterpriseNetwork.NYPSOC.ncl.sg', gpolicy) == True
assert can_access('user2', 'n2.EnterpriseNetwork.NYPSOC.ncl.sg', gpolicy) == False
assert can_access('user3', 'n2.EnterpriseNetwork.NYPSOC.ncl.sg', gpolicy) == False
assert can_access('suser', 'n2.EnterpriseNetwork.NYPSOC.ncl.sg', gpolicy) == True
def main():
'''The func is the main func.'''
import sys
if (len(sys.argv) == 2) and (sys.argv[1] == "test"):
test()
print "Pass all test"
exit()
if (len(sys.argv) == 2) and (sys.argv[1] == "do"):
gp1 = { }
node_cmd_list = group_exp(gp1)
ename = gp1['ExperimentDomainName']
for (node, cmd) in node_cmd_list.items():
cmdline = "echo '%s' | ssh %s.%s" % (cmd, node, ename)
subprocess.call(cmdline, shell = True)
if __name__ == "__main__":
main()

View File

@ -30,6 +30,15 @@ try:
except ImportError:
from cgi import parse_qs
from urlparse import urlparse
# Switch to open function let URL path define path.
# &path=vncserver.domain.name:port
URL_PATH_DEF_VNCSERVER = True
# Whether is URL path encoded
URL_PATH_ENCODED = False
# URL valid day number after creating.
URL_VALID_DAYNUM = 1
# Salt used to valide the hash of the URL.
URL_SALT = "Some salt for security. Please change it in your project. liqun@ncl.sg"
class ProxyRequestHandler(websockifyserver.WebSockifyRequestHandler):
@ -104,6 +113,15 @@ Traffic Legend:
msg = "connecting to command: '%s' (port %s)" % (" ".join(self.server.wrap_cmd), self.server.target_port)
elif self.server.unix_target:
msg = "connecting to unix socket: %s" % self.server.unix_target
elif URL_PATH_DEF_VNCSERVER:
import encode_url
(ptarget_host, ptarget_port) = encode_url.get_server_from_path(\
self.path, URL_PATH_ENCODED, URL_VALID_DAYNUM, URL_SALT)
if ptarget_port == 0:
print 'Error: URL encode error[%s]' % ptarget_host
return
#raise self.server.EClose('Cannot decode path.')
msg = "connecting to: %s:%s" % (ptarget_host, ptarget_port)
else:
msg = "connecting to: %s:%s" % (
self.server.target_host, self.server.target_port)
@ -112,7 +130,14 @@ Traffic Legend:
msg += " (using SSL)"
self.log_message(msg)
tsock = websockifyserver.WebSockifyServer.socket(self.server.target_host,
if URL_PATH_DEF_VNCSERVER:
tsock = websockifyserver.WebSockifyServer.socket(ptarget_host,
ptarget_port,
connect=True,
use_ssl=self.server.ssl_target,
unix_socket=self.server.unix_target)
else:
tsock = websockifyserver.WebSockifyServer.socket(self.server.target_host,
self.server.target_port,
connect=True,
use_ssl=self.server.ssl_target,