major rewrite of the auth function

This commit is contained in:
deflax 2016-03-31 17:40:40 +03:00
parent 20052a773d
commit 1f405f7990
4 changed files with 89 additions and 56 deletions

View file

@ -16,23 +16,28 @@ def addclient(vmid, vmname, clientid, clientname, clientemail, vmpass):
clientsdb = readclientsdb() clientsdb = readclientsdb()
if str(clientid) in clientsdb: if str(clientid) in clientsdb:
ioconfig.logger.info('clients> client ' + clientid + ' already exists. merging.') ioconfig.logger.info('client[{}]> already exist. merging.'.format(clientid))
else: else:
ioconfig.logger.info('clients> client ' + clientid + ' does not exist. creating.') ioconfig.logger.info('client[{}]> does not exist. creating...'.format(clientid))
vcard = { 'name':str(clientname), 'email':str(clientemail) } #generate password and send it to the client
newpass = utils.genpassword(30)
salt = bcrypt.gensalt()
b_newpass = newpass.encode('utf-8')
encpasswd = bcrypt.hashpw(b_newpass, salt).decode('utf-8')
vcard = { 'name':str(clientname), 'email':str(clientemail), 'encpasswd':str(encpasswd) }
newclient = { str(clientid):vcard } newclient = { str(clientid):vcard }
clientsdb.update(newclient) clientsdb.update(newclient)
ioconfig.logger.info('clients> vmid {} owner set to {} (id: {}, email: {})'.format(vmid, clientname, clientid, clientemail)) #TODO: 1. Send initial email to the user as we wont use the internal auth from now on.
#TODO: 2. Sync with proxmaster-admin database (shell command could be used for this one)
ioconfig.logger.info('client[{}]> vmid {} is now owned by {} ({})'.format(clientemail, vmid, clientid, clientname))
#create initial vm template #create initial vm template
vmdata = { 'hostname':str(vmname), 'vmid':str(vmid), 'ownerid':str(clientid) } vmdata = { 'hostname':str(vmname), 'vmid':str(vmid), 'ownerid':str(clientid) }
clientsdb[str(clientid)][str(vmid)] = vmdata clientsdb[str(clientid)][str(vmid)] = vmdata
writeclientsdb(clientsdb) writeclientsdb(clientsdb)
#set password for the first time...
setencpasswd(vmname, vmpass)
def setencpasswd(vmname, newpass): def setencpasswd(clientemail, newpass):
""" setup a new management password """ """ setup a new management password """
salt = bcrypt.gensalt() salt = bcrypt.gensalt()
b_newpass = newpass.encode('utf-8') b_newpass = newpass.encode('utf-8')
@ -41,62 +46,69 @@ def setencpasswd(vmname, newpass):
try: try:
clientsdb = readclientsdb() clientsdb = readclientsdb()
#print(clientsdb) #print(clientsdb)
path = utils.get_path(clientsdb, vmname) path = utils.get_path(clientsdb, clientemail)
#print(path) #print(path)
c_id = str(path[0]) c_id = str(path[0])
v_id = str(path[1])
#check the returned path with forward query #check the returned path with forward query
query = clientsdb[c_id][v_id]['hostname'] query = clientsdb[c_id]['email']
except: except:
raise raise
if query != vmname: if query != clientemail:
ioconfig.logger.critical('clients> test query returns different vmname! check clients.json consistency!') ioconfig.logger.critical('clients.db> test query returns different vmname! check clients db for consistency!')
raise raise
else: else:
clientsdb[c_id][v_id]['encpasswd'] = encpasswd clientsdb[c_id]['encpasswd'] = encpasswd
ioconfig.logger.info('clients> {} (clientid: {}, vmid: {}) got its management password changed!'.format(query, c_id, v_id)) ioconfig.logger.info('client[{}]> got its management password changed!'.format(clientemail))
writeclientsdb(clientsdb) writeclientsdb(clientsdb)
#TODO: change lxc container password #TODO: Send new email to the client to notify the password change. This time sending the password in plain text is not needed.
def validate(clientemail, srvpass): def validate(clientemail, password):
""" return vmid or false if credentials match something in clientdb. useful for authing extrnal admin panels """ """ return list of owned vmids or false if credentials match an user form the database.
useful for authing extrnal admin panels """
#1. search for the client
try: try:
clientsdb = readclientsdb() clientsdb = readclientsdb()
path = utils.get_path(clientsdb, clientemail) path = utils.get_path(clientsdb, clientemail)
c_id = str(path[0]) c_id = str(path[0])
#check the returned path with forward query #check the returned path with forward query
ioconfig.logger.info('clients> {} was found with clientid: {}'.format(clientemail, c_id)) ioconfig.logger.info('client[{}]> found. path={}'.format(clientemail, str(path)))
except: except:
raise raise
ioconfig.logger.warning('clients> {} was not found in the database!'.format(clientemail)) ioconfig.logger.warning('clients.db> {} was not found in the database!'.format(clientemail))
#log bad ips here... #log bad ips here...
return False return False
vmlist = clientsdb[c_id] #2. check the password
encpass = clientsdb[c_id]['encpasswd']
b_srvpass = password.encode('utf-8')
b_encpass = encpass.encode('utf-8')
if (hmac.compare_digest(bcrypt.hashpw(b_srvpass, b_encpass), b_encpass)):
#login successful
ioconfig.logger.info('client[{}]> logged in successfully'.format(clientemail))
#TODO: Notify admin
#3. generate vmlist to return the owned ids to the client.
return clientvms(clientsdb[c_id])
else:
ioconfig.logger.warning('clients> {} ACCESS DENIED!'.format(vmid))
#cant compare password
#TODO: Log attempts and block.
return {}
def clientvms(vmlist):
""" generate vmlist """
#clear unused objects. perhaps there is a better way to do this but im kinda anxious today... #clear unused objects. perhaps there is a better way to do this but im kinda anxious today...
vmlist.pop('name') vmlist.pop('name')
vmlist.pop('email') vmlist.pop('email')
#try each vmid owned by this user for a password match response = []
for vmid,data in vmlist.items(): for vmid,data in vmlist.items():
print(vmid) print(vmid)
print(data) print(data)
#try to capture the encrypted password
encpass = data['encpasswd']
b_srvpass = srvpass.encode('utf-8')
b_encpass = encpass.encode('utf-8')
if (hmac.compare_digest(bcrypt.hashpw(b_srvpass, b_encpass), b_encpass)):
#login successful
ioconfig.logger.info('clients> {} was validated successfully by {}'.format(vmid, clientemail))
response = { 'vmid':vmid } response = { 'vmid':vmid }
else:
ioconfig.logger.warning('clients> {} ACCESS DENIED!'.format(vmid))
#cant compare password
response = { }
#TODO: this will require major rewrite again.. or it will fail to auth 2 machines with same password. lame..
return response return response
@ -136,5 +148,5 @@ def writeclientsdb(clientsdb):
if __name__ == '__main__': if __name__ == '__main__':
#setencpasswd('srv.test1.com', 'todos') setencpasswd('daniel@deflax.net', 'todos')
validate('daniel@deflax.net', 'todos') print(validate('daniel@deflax.net', 'todos'))

View file

@ -168,7 +168,7 @@ def query_region(region_name):
for region in all_regions: for region in all_regions:
if grid_data[region]['region'] == region_name: if grid_data[region]['region'] == region_name:
logger.info('grid> region ' + region_name + ' found') logger.info('region[{}]> found: id={}'.format(region_name, region))
return grid_data[region]['id'] return grid_data[region]['id']
break break
logger.error('grid> cant find region ' + region_name) logger.error('grid> cant find region ' + region_name)
@ -339,10 +339,10 @@ def query_vm(req_vmid):
try: try:
vm_type = grid_data[str(region_id)][str(slave_id)][str(target)]['type'] vm_type = grid_data[str(region_id)][str(slave_id)][str(target)]['type']
except: except:
logger.error('{}> type is unknown!'.format(vm_id)) logger.error('vm[{}]> type is unknown!'.format(vm_id))
raise raise
logger.info('{}> region={}, slave={}, type={} found.'.format(target, region_id, slave_id, vm_type)) logger.info('vm[{}]> type {} found. path=region={} found.'.format(target, vm_type, str(path)))
return slave_id, vm_type return slave_id, vm_type

View file

@ -148,13 +148,13 @@ def vmstart(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> starting %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> starting %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.start.post() result = proxobject.nodes(slave_name).qemu(vm_id).status.start.post()
if vm_type == 'lxc': if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.start.post() result = proxobject.nodes(slave_name).lxc(vm_id).status.start.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result)) #ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'START' } response = { 'status':'START' }
return response return response
@ -165,13 +165,13 @@ def vmshutdown(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> acpi shutdown %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> acpi shutdown %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post() result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post()
if vm_type == 'lxc': if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.stop.post() result = proxobject.nodes(slave_name).lxc(vm_id).status.stop.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result)) #ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'SHUTDOWN', 'vmid':vm_id } response = { 'status':'SHUTDOWN', 'vmid':vm_id }
return response return response
@ -182,13 +182,13 @@ def vmstop(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> power off %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> power off %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post() result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post()
if vm_type == 'lxc': if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.stop.post() result = proxobject.nodes(slave_name).lxc(vm_id).status.stop.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result)) #ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'STOP', 'vmid':vm_id } response = { 'status':'STOP', 'vmid':vm_id }
return response return response
@ -199,13 +199,13 @@ def vmshutdown(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> acpi shutdown sent to %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> acpi shutdown sent to %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.shutdown.post() result = proxobject.nodes(slave_name).qemu(vm_id).status.shutdown.post()
if vm_type == 'lxc': if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.shutdown.post() result = proxobject.nodes(slave_name).lxc(vm_id).status.shutdown.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result)) #ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'SHUTDOWN', 'vmid':vm_id } response = { 'status':'SHUTDOWN', 'vmid':vm_id }
return response return response
@ -216,13 +216,13 @@ def vmsuspend(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> suspending %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> suspending %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.suspend.post() result = proxobject.nodes(slave_name).qemu(vm_id).status.suspend.post()
if vm_type == 'lxc': if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.suspend.post() result = proxobject.nodes(slave_name).lxc(vm_id).status.suspend.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result)) #ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'SUSPEND', 'vmid':vm_id } response = { 'status':'SUSPEND', 'vmid':vm_id }
return response return response
@ -233,13 +233,13 @@ def vmresume(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> resuming %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> resuming %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
result = proxobject.nodes(slave_name).qemu(vm_id).status.resume.post() result = proxobject.nodes(slave_name).qemu(vm_id).status.resume.post()
if vm_type == 'lxc': if vm_type == 'lxc':
result = proxobject.nodes(slave_name).lxc(vm_id).status.resume.post() result = proxobject.nodes(slave_name).lxc(vm_id).status.resume.post()
#ioconfig.logger.info('grid[{}]> {}'.format(slave_name, result)) #ioconfig.logger.info('slave[{}]> {}'.format(slave_name, result))
response = { 'status':'RESUME', 'vmid':vm_id } response = { 'status':'RESUME', 'vmid':vm_id }
return response return response
@ -250,7 +250,7 @@ def vmvnc(vm_id):
proxobject = auth(slave_id) proxobject = auth(slave_id)
vm_type = vm_type.lower() vm_type = vm_type.lower()
slave_name = proxobject.cluster.status.get()[0]['name'] slave_name = proxobject.cluster.status.get()[0]['name']
ioconfig.logger.info('grid[%s]> invoking vnc ticket for %s %s' % (slave_name, vm_type, vm_id)) ioconfig.logger.info('slave[%s]> invoking vnc ticket for %s %s' % (slave_name, vm_type, vm_id))
if vm_type == 'kvm': if vm_type == 'kvm':
ticket = proxobject.nodes(slave_name).qemu(vm_id).vncproxy.post(websocket=1) ticket = proxobject.nodes(slave_name).qemu(vm_id).vncproxy.post(websocket=1)
@ -282,7 +282,7 @@ def vmvnc(vm_id):
external_url = ioconfig.parser.get('general', 'novnc_url') external_url = ioconfig.parser.get('general', 'novnc_url')
prefix = external_url + "/?host=" + myip + "&port=" + listenport + "&encrypt=0&true_color=1&password=" prefix = external_url + "/?host=" + myip + "&port=" + listenport + "&encrypt=0&true_color=1&password="
vnc_url = prefix + ticket['ticket'] vnc_url = prefix + ticket['ticket']
ioconfig.logger.info('grid[{}]> {}'.format(slave_name, vnc_url)) ioconfig.logger.info('slave[{}]> {}'.format(slave_name, vnc_url))
response = { 'status':'VNC', 'fqdn':external_url, 'host':myip, 'port':listenport, 'encrypt':'0', 'true_color':'1', 'ticket':ticket['ticket'] } response = { 'status':'VNC', 'fqdn':external_url, 'host':myip, 'port':listenport, 'encrypt':'0', 'true_color':'1', 'ticket':ticket['ticket'] }
return response return response

View file

@ -2,8 +2,27 @@
# #
# helper functions # helper functions
from copy import deepcopy
import functools import functools
from copy import deepcopy
from random import SystemRandom
def genpassword(length=20):
""" generates pseudo-random password """
choice = SystemRandom().choice
charsets = [
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789',
'%&=?+~#-_.',
]
pwd = []
charset = choice(charsets)
while len(pwd) < length:
pwd.append(choice(charset))
charset = choice(list(set(charsets) - set([charset])))
return "".join(pwd)
def dict_merge(target, *args): def dict_merge(target, *args):
""" Recursively merges mutiple dicts """ """ Recursively merges mutiple dicts """
@ -24,6 +43,7 @@ def dict_merge(target, *args):
target[k] = deepcopy(v) target[k] = deepcopy(v)
return target return target
def find_rec(search_dict, field): def find_rec(search_dict, field):
""" """
Takes a dict with nested lists and dicts, Takes a dict with nested lists and dicts,
@ -68,3 +88,4 @@ def chained_get(dct, *keys):
return 'NA' if level is SENTRY else level.get(key, SENTRY) return 'NA' if level is SENTRY else level.get(key, SENTRY)
return functools.reduce(getter, keys, dct) return functools.reduce(getter, keys, dct)