diff --git a/clientsdb.py b/clientsdb.py index 62b3059..6fb11c6 100644 --- a/clientsdb.py +++ b/clientsdb.py @@ -16,23 +16,28 @@ def addclient(vmid, vmname, clientid, clientname, clientemail, vmpass): clientsdb = readclientsdb() if str(clientid) in clientsdb: - ioconfig.logger.info('clients> client ' + clientid + ' already exists. merging.') + ioconfig.logger.info('client[{}]> already exist. merging.'.format(clientid)) else: - ioconfig.logger.info('clients> client ' + clientid + ' does not exist. creating.') - vcard = { 'name':str(clientname), 'email':str(clientemail) } + ioconfig.logger.info('client[{}]> does not exist. creating...'.format(clientid)) + #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 } 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 vmdata = { 'hostname':str(vmname), 'vmid':str(vmid), 'ownerid':str(clientid) } clientsdb[str(clientid)][str(vmid)] = vmdata writeclientsdb(clientsdb) - #set password for the first time... - setencpasswd(vmname, vmpass) -def setencpasswd(vmname, newpass): +def setencpasswd(clientemail, newpass): """ setup a new management password """ salt = bcrypt.gensalt() b_newpass = newpass.encode('utf-8') @@ -41,62 +46,69 @@ def setencpasswd(vmname, newpass): try: clientsdb = readclientsdb() #print(clientsdb) - path = utils.get_path(clientsdb, vmname) + path = utils.get_path(clientsdb, clientemail) #print(path) c_id = str(path[0]) - v_id = str(path[1]) #check the returned path with forward query - query = clientsdb[c_id][v_id]['hostname'] + query = clientsdb[c_id]['email'] except: raise - if query != vmname: - ioconfig.logger.critical('clients> test query returns different vmname! check clients.json consistency!') + if query != clientemail: + ioconfig.logger.critical('clients.db> test query returns different vmname! check clients db for consistency!') raise else: - clientsdb[c_id][v_id]['encpasswd'] = encpasswd - ioconfig.logger.info('clients> {} (clientid: {}, vmid: {}) got its management password changed!'.format(query, c_id, v_id)) + clientsdb[c_id]['encpasswd'] = encpasswd + ioconfig.logger.info('client[{}]> got its management password changed!'.format(clientemail)) 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): - """ return vmid or false if credentials match something in clientdb. useful for authing extrnal admin panels """ +def validate(clientemail, password): + """ 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: clientsdb = readclientsdb() path = utils.get_path(clientsdb, clientemail) c_id = str(path[0]) #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: 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... 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... vmlist.pop('name') vmlist.pop('email') - - #try each vmid owned by this user for a password match + + response = [] for vmid,data in vmlist.items(): print(vmid) - 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 } - 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.. + response = { 'vmid':vmid } return response @@ -136,5 +148,5 @@ def writeclientsdb(clientsdb): if __name__ == '__main__': - #setencpasswd('srv.test1.com', 'todos') - validate('daniel@deflax.net', 'todos') + setencpasswd('daniel@deflax.net', 'todos') + print(validate('daniel@deflax.net', 'todos')) diff --git a/grid.py b/grid.py index 34e302b..e4e9ea6 100644 --- a/grid.py +++ b/grid.py @@ -168,7 +168,7 @@ def query_region(region_name): for region in all_regions: 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'] break logger.error('grid> cant find region ' + region_name) @@ -339,10 +339,10 @@ def query_vm(req_vmid): try: vm_type = grid_data[str(region_id)][str(slave_id)][str(target)]['type'] except: - logger.error('{}> type is unknown!'.format(vm_id)) + logger.error('vm[{}]> type is unknown!'.format(vm_id)) 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 diff --git a/plugin.py b/plugin.py index d789ac6..1a2378b 100644 --- a/plugin.py +++ b/plugin.py @@ -148,13 +148,13 @@ def vmstart(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': result = proxobject.nodes(slave_name).qemu(vm_id).status.start.post() if vm_type == 'lxc': 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' } return response @@ -165,13 +165,13 @@ def vmshutdown(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post() if vm_type == 'lxc': 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 } return response @@ -182,13 +182,13 @@ def vmstop(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': result = proxobject.nodes(slave_name).qemu(vm_id).status.stop.post() if vm_type == 'lxc': 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 } return response @@ -199,13 +199,13 @@ def vmshutdown(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': result = proxobject.nodes(slave_name).qemu(vm_id).status.shutdown.post() if vm_type == 'lxc': 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 } return response @@ -216,13 +216,13 @@ def vmsuspend(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': result = proxobject.nodes(slave_name).qemu(vm_id).status.suspend.post() if vm_type == 'lxc': 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 } return response @@ -233,13 +233,13 @@ def vmresume(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': result = proxobject.nodes(slave_name).qemu(vm_id).status.resume.post() if vm_type == 'lxc': 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 } return response @@ -250,7 +250,7 @@ def vmvnc(vm_id): proxobject = auth(slave_id) vm_type = vm_type.lower() 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': 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') prefix = external_url + "/?host=" + myip + "&port=" + listenport + "&encrypt=0&true_color=1&password=" 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'] } return response diff --git a/utils.py b/utils.py index 8746681..a03db64 100644 --- a/utils.py +++ b/utils.py @@ -2,8 +2,27 @@ # # helper functions -from copy import deepcopy 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): """ Recursively merges mutiple dicts """ @@ -24,6 +43,7 @@ def dict_merge(target, *args): target[k] = deepcopy(v) return target + def find_rec(search_dict, field): """ 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 functools.reduce(getter, keys, dct) +