def on_waptclient_connect(): try: uuid = request.args.get('uuid', None) if not uuid: raise EWaptForbiddden('Missing source host uuid') allow_unauthenticated_connect = app.conf.get( 'allow_unauthenticated_connect', False) if not allow_unauthenticated_connect: try: token_gen = get_secured_token_generator() token_data = token_gen.loads(request.args['token']) uuid = token_data.get('uuid', None) if not uuid: raise EWaptAuthenticationFailure('Bad host UUID') if token_data['server_uuid'] != get_server_uuid(): raise EWaptAuthenticationFailure('Bad server UUID') except Exception as e: raise EWaptAuthenticationFailure( u'SocketIO connection not authorized, invalid token: %s' % e) logger.info( u'Socket.IO connection from wapt client sid %s (uuid: %s fqdn:%s)' % (request.sid, uuid, token_data.get('computer_fqdn'))) else: logger.info( u'Unauthenticated Socket.IO connection from wapt client sid %s (uuid: %s)' % (request.sid, uuid)) with WaptDB(): # update the db with wapt_db.atomic() as trans: # stores sid in database hostcount = Hosts.update( server_uuid=get_server_uuid(), listening_protocol='websockets', listening_address=request.sid, listening_timestamp=datetime2isodate(), last_seen_on=datetime2isodate()).where( Hosts.uuid == uuid).execute() # if not known, reject the connection if hostcount == 0: raise EWaptForbiddden('Host is not registered') session['uuid'] = uuid return True except Exception as e: if 'uuid' in session: session.pop('uuid') logger.warning( u'SocketIO connection refused for uuid %s, sid %s: %s, instance %s' % (uuid, request.sid, e, app.conf.get('application_root'))) disconnect() return False
def on_wapt_pong(): uuid = None try: uuid = session.get('uuid') if not uuid: logger.critical(u'SocketIO %s connected but no host uuid in session: asking connected host to reconnect' % (request.sid)) emit('wapt_force_reconnect') return False else: logger.debug(u'Socket.IO pong from wapt client sid %s (uuid: %s)' % (request.sid, session.get('uuid',None))) # stores sid in database try: if wapt_db.is_closed(): wapt_db.connect() with wapt_db.atomic() as trans: hostcount = Hosts.update( server_uuid=get_server_uuid(), listening_timestamp=datetime2isodate(), listening_protocol='websockets', listening_address=request.sid, reachable='OK', ).where(Hosts.uuid == uuid).execute() # if not known, reject the connection if hostcount == 0: logger.warning(u'SocketIO sid %s connected but no match in database for uuid %s : asking to reconnect' % (request.sid,uuid)) emit('wapt_force_reconnect') return False finally: if not wapt_db.is_closed(): wapt_db.close() return True except Exception as e: logger.critical(u'SocketIO pong error for uuid %s and sid %s : %s, instance: %s' % (uuid,request.sid,traceback.format_exc(),app.conf.get('application_root'))) return False
def proxy_host_request(request, action): """Proxy a waptconsole action to wapt clients using websockets Args: uuid: can be a list or a single uuid notify_user: 0/1 notify_server: 0/1 Returns: dict: 'result': 'success' (list) 'errors' (list) 'msg' (str) 'success'(bool) 'request_time' (float) 'error_code' """ try: start_time = time.time() all_args = {k: v for k, v in request.args.iteritems()} if request.json: all_args.update(request.json) uuids = ensure_list(all_args['uuid']) del(all_args['uuid']) timeout = float(request.args.get('timeout', app.conf.get('clients_read_timeout', 5))) result = dict(success=[], errors=[]) tasks = [] for uuid in uuids: try: host_data = Hosts\ .select(Hosts.uuid, Hosts.computer_fqdn, Hosts.server_uuid, Hosts.listening_address, Hosts.listening_port, Hosts.listening_protocol, Hosts.listening_timestamp, )\ .where((Hosts.server_uuid == get_server_uuid()) & (Hosts.uuid == uuid) & (~Hosts.listening_address.is_null()) & (Hosts.listening_protocol == 'websockets'))\ .dicts()\ .first(1) if host_data and host_data.get('listening_address', None): msg = u'' logger.info( 'Launching %s with args %s for %s at address %s...' % (action, all_args, uuid, host_data['listening_address'])) args = dict(all_args) sid = host_data['listening_address'] computer_fqdn = host_data['computer_fqdn'] def emit_action(sid, uuid, action, action_args, computer_fqdn, timeout=5): try: got_result = [] def result_callback(data): got_result.append(data) logger.debug(u'Emit %s to %s (%s)' % (action, uuid, computer_fqdn)) socketio.emit(action, action_args, room=sid, callback=result_callback) # wait for asynchronous answer... wait_loop = timeout * 20 while not got_result: wait_loop -= 1 if wait_loop < 0: raise EWaptTimeoutWaitingForResult(u'Timeout, client did not send result within %s s' % timeout) socketio.sleep(0.05) # action succedded result['success'].append( dict( uuid=uuid, msg=msg, computer_fqdn=computer_fqdn, result=got_result[0], )) except Exception as e: result['errors'].append( dict( uuid=uuid, msg='%s' % repr(e), computer_fqdn='', )) if sid: tasks.append(socketio.start_background_task( emit_action, sid=sid, uuid=uuid, action=action, action_args=args, computer_fqdn=computer_fqdn)) else: result['errors'].append( dict( uuid=uuid, msg=u'Host %s is not registered or not connected wia websockets' % uuid, computer_fqdn='', )) except Exception as e: result['errors'].append( dict( uuid=uuid, msg=u'Host %s error %s' % (uuid, repr(e)), computer_fqdn='', )) # wait for all background tasks to terminate or timeout... # assume the timeout should be longer if more hosts to perform wait_loop = timeout * len(tasks) while True: running = [t for t in tasks if t.is_alive()] if not running: break wait_loop -= 1 if wait_loop < 0: break socketio.sleep(0.05) msg = ['Success : %s, Errors: %s' % (len(result['success']), len(result['errors']))] if result['errors']: msg.extend(['%s: %s' % (e['computer_fqdn'], e['msg']) for e in result['errors']]) return make_response(result, msg='\n- '.join(msg), success=len(result['success']) > 0, request_time=time.time() - start_time) except Exception as e: return make_response_from_exception(e)
def check_auth( username=None, password = None, request = None, session=None, methods=['admin','ldap','session']): """This function is called to check if a username / password combination is valid or to get already authenticated username from session argument. If no username or password, use session.authorization header if available. Args: username (str): password (str): request (Flask.Request) : request where to lookup authrization basic header session (Flask.Session) where to lookup session user methods (list of str) : list of auth method to try until one is successfull 'session': use current session user if it matches username 'admin': 'ldap': 'kerb': 'ssl': 'passwd': Returns: dict (auth_method = auth_method,user=auth_user,auth_date=auth_date) None if auth is not successfull """ if username is None and password is None and request is not None and request.authorization: username = request.authorization.username password = request.authorization.password auth_method = None auth_user = None auth_date = None assert(isinstance(methods,list)) for method in methods: if method == 'session' and session: session_user = session.get('user',None) if session_user and (username is None or username == session_user): auth_user = session_user auth_date = session.get('auth_date',None) auth_method = method logger.debug(u'User %s authenticated using session cookie' % (username,)) elif method == 'passwd' and valid_username(username) and username != app.conf['wapt_user'] and password is not None: # local htpasswd user/passwd file for add_host registration action if app.conf.get('htpasswd_path'): htpasswd_users = HtpasswdFile(app.conf.get('htpasswd_path')) if htpasswd_users.verify(username,password): auth_method = method auth_user = username auth_date = datetime.datetime.utcnow().isoformat() logger.debug(u'User %s authenticated using htpasswd %s' % (username,htpasswd_users)) else: logger.debug(u'user %s htpasswd %s verification failed' % (username,htpasswd_users)) elif method == 'admin' and valid_username(username) and app.conf['wapt_user'] == username and password is not None: pbkdf2_sha256_ok = False pass_sha512_crypt_ok = False pass_bcrypt_crypt_ok = False if '$pbkdf2-sha256$' in app.conf['wapt_password']: pbkdf2_sha256_ok = pbkdf2_sha256.verify(password, app.conf['wapt_password']) elif sha512_crypt.identify(app.conf['wapt_password']): pass_sha512_crypt_ok = sha512_crypt.verify( password, app.conf['wapt_password']) else: try: if bcrypt.identify(app.conf['wapt_password']): pass_bcrypt_crypt_ok = bcrypt.verify( password, app.conf['wapt_password']) except Exception: pass if pbkdf2_sha256_ok or pass_sha512_crypt_ok or pass_bcrypt_crypt_ok: auth_method = method auth_user = username auth_date = datetime.datetime.utcnow().isoformat() else: logger.debug(u'wapt admin passwd verification failed') elif method == 'token': # token auth token_secret_key = app.conf['secret_key'] if token_secret_key: # check if there is a valid token in the password try: token_gen = get_secured_token_generator(token_secret_key) # check if there is a Bearer, else use Basic / password authorization = request.headers.get('Authorization') if authorization and authorization.startswith('Bearer '): token_data = token_gen.loads(authorization.split(' ',1)[1]) elif password is not None: token_data = token_gen.loads(password) else: raise EWaptAuthenticationFailure('No token') uuid = token_data.get('uuid', token_data.get('user',None)) if not uuid: raise EWaptAuthenticationFailure('Bad token UUID') if token_data['server_uuid'] != get_server_uuid(): raise EWaptAuthenticationFailure('Bad server UUID') auth_method = method auth_user = uuid auth_date = datetime.datetime.fromtimestamp(token_data['iat']).isoformat() logger.debug(u'User %s authenticated using token' % (uuid,)) except Exception as e: logger.debug(u'Token verification failed : %s' % repr(e)) pass elif method == 'kerb' and app.conf['use_kerberos']: # with nginx kerberos module, auth user name is stored as Basic auth in the # 'Authorisation' header with password 'bogus_auth_gss_passwd' # Kerberos auth negociated by nginx if username != '' and password == 'bogus_auth_gss_passwd': authenticated_user = username.lower().replace('$', '') auth_method = method auth_user = authenticated_user auth_date = datetime.datetime.utcnow().isoformat() logger.debug(u'User %s authenticated using kerberos' % (authenticated_user,)) elif method == 'ldap' and valid_username(username) and password is not None: if auth_module_ad is not None and (app.conf['wapt_user'] != username and auth_module_ad.check_credentials_ad(app.conf, username, password)): auth_method = method auth_user = username auth_date = datetime.datetime.utcnow().isoformat() logger.debug(u'User %s authenticated using LDAP' % (username,)) # nginx ssl auth. elif method == 'ssl' and request and app.conf['use_ssl_client_auth'] and request.headers.get('X-Ssl-Authenticated', None) == 'SUCCESS': dn = request.headers.get('X-Ssl-Client-Dn', None) if dn: auth_method = method # strip CN= if no other suffix. if dn.upper().startswith('CN='): auth_user = dn.split('=',1)[1] else: auth_user = dn auth_date = datetime.datetime.utcnow().isoformat() logger.debug(u'User %s authenticated using SSL client certificate' % (dn,)) if auth_method and auth_user: break #only session and admin methods are ok for the embedded wapt admin password based account. if auth_method and auth_user and (auth_user != app.conf['wapt_user'] or auth_method in ('session','admin')): return dict(auth_method = auth_method,user=auth_user,auth_date=auth_date) else: return None