Beispiel #1
0
    def run(self):
        if self.gate.restricted:
            self.demote('nobody')
        else:
            if self.gate.initial_identity:
                AuthenticationService.get(self.context).login(
                    self.gate.initial_identity, demote=True)

        try:
            socket_namespaces = {}
            while True:
                rq = self.stream.recv()
                if not rq:
                    return
                if rq.object['type'] == 'http':
                    gevent.spawn(self.handle_http_request, rq)
                if rq.object['type'] == 'socket':
                    msg = rq.object['message']
                    nsid = rq.object['namespace']
                    event = rq.object['event']

                    if event == 'connect':
                        socket_namespaces[nsid] = WorkerSocketNamespace(
                            self.context, nsid)

                    socket_namespaces[nsid].process_event(event, msg)

                    if event == 'disconnect':
                        socket_namespaces[nsid].destroy()
                        logging.debug(
                            'Socket disconnected, destroying endpoints')
        # pylint: disable=W0703
        except Exception:
            logging.error('Worker crashed!')
            traceback.print_exc()
Beispiel #2
0
    def handle_api_identity(self, http_context):
        """
        Collect user and server informations from authentication service and
        ajenti config file.

        :param http_context: HttpContext
        :type http_context: HttpContext
        :return: User and server informations and profil.
        :rtype: dict
        """

        return {
            'identity': {
                'user':
                AuthenticationService.get(self.context).get_identity(),
                'uid':
                os.getuid(),
                'effective':
                os.geteuid(),
                'elevation_allowed':
                aj.config.data['auth'].get('allow_sudo', False),
                'profile':
                AuthenticationService.get(
                    self.context).get_provider().get_profile(
                        AuthenticationService.get(
                            self.context).get_identity()),
            },
            'machine': {
                'name': aj.config.data['name'],
                'hostname': socket.gethostname(),
            },
            'color': aj.config.data.get('color', None),
        }
Beispiel #3
0
    def handle_api_logout(self, http_context):
        """
        Logout by closing associated worker.

        :param http_context: HttpContext
        :type http_context: HttpContext
        """

        AuthenticationService.get(self.context).get_provider().signout()
        self.context.worker.terminate()
Beispiel #4
0
    def run(self):
        if self.gate.restricted:
            restricted_user = aj.config.data['restricted_user']
            self.demote(pwd.getpwnam(restricted_user).pw_uid)
        else:
            if self.gate.initial_identity:
                AuthenticationService.get(self.context).login(
                    self.gate.initial_identity, demote=True)

        try:
            socket_namespaces = {}
            while True:
                rq = self.stream.recv()

                if not rq:
                    return

                if rq.object['type'] == 'http':
                    gevent.spawn(self.handle_http_request, rq)

                if rq.object['type'] == 'socket':
                    msg = rq.object['message']
                    nsid = rq.object['namespace']
                    event = rq.object['event']

                    if event == 'connect':
                        socket_namespaces[nsid] = WorkerSocketNamespace(
                            self.context, nsid)

                    socket_namespaces[nsid].process_event(event, msg)

                    if event == 'disconnect':
                        socket_namespaces[nsid].destroy()
                        logging.debug(
                            'Socket disconnected, destroying endpoints')

                if rq.object['type'] == 'config-data':
                    logging.debug('Received a config update')
                    aj.config.data = rq.object['data']['config']
                    aj.users.data = rq.object['data']['users']
                    self._master_config_reloaded.set()

                if rq.object['type'] == 'session-list':
                    logging.debug('Received a session list update')
                    aj.sessions = rq.object['data']

        # pylint: disable=W0703
        except Exception:
            logging.error('Worker crashed!')
            traceback.print_exc()
Beispiel #5
0
    def update_password(self, http_context):
        """
        Update user's password in the auth provider.

        :param http_context: HttpContext
        :type http_context: HttpContext
        """

        serial = json.loads(http_context.body.decode())['serial']
        password = json.loads(http_context.body.decode())['password']

        if serial and password:
            with open(SECRET_FILE, 'r') as f:
                secret = f.read().strip('\n')
                serializer = URLSafeTimedSerializer(secret)
            user = serializer.loads(serial, max_age=900)['user']
            auth_provider = AuthenticationService.get(
                self.context).get_provider()
            answer = auth_provider.update_password(user, password)
            if not answer:
                http_context.respond_forbidden()
                return [b'403 Forbidden']
            else:
                http_context.respond_ok()
                return [b'200 OK']
Beispiel #6
0
    def handle_api_auth(self, http_context):
        """
        Test user authentication to login or to elevate privileges.

        :param http_context: HttpContext
        :type http_context: HttpContext
        :return: Success status and username
        :rtype: dict
        """

        body_data = json.loads(http_context.body.decode())
        mode = body_data['mode']
        username = body_data.get('username', None)
        password = body_data.get('password', None)

        auth = AuthenticationService.get(self.context)

        if mode == 'normal':
            auth_info = auth.check_password(username, password)
            if auth_info:
                auth.prepare_session_redirect(http_context, username,
                                              auth_info)
                return {
                    'success': True,
                    'username': username,
                }

            gevent.sleep(3)
            return {
                'success': False,
                'error': None,
            }

        elif mode == 'sudo':
            target = 'root'
            try:
                if auth.check_sudo_password(username, password):
                    self.context.worker.terminate()
                    auth.prepare_session_redirect(http_context, target, None)
                    return {
                        'success': True,
                        'username': target,
                    }

                gevent.sleep(3)
                return {
                    'success': False,
                    'error': _('Authorization failed'),
                }
            except SudoError as e:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': e.message,
                }
        return {
            'success': False,
            'error': 'Invalid mode',
        }
Beispiel #7
0
 def handle_api_change_password(self, http_context):
     provider = AuthenticationService.get(self.context).get_provider()
     try:
         provider.change_password(self.context.identity,
                                  http_context.json_body()['password'],
                                  http_context.json_body()['new_password'])
     except Exception as e:
         raise EndpointError(None, str(e))
Beispiel #8
0
    def run(self):
        if self.gate.restricted:
            restricted_user = aj.config.data['restricted_user']
            self.demote(pwd.getpwnam(restricted_user).pw_uid)
        else:
            if self.gate.initial_identity:
                AuthenticationService.get(self.context).login(
                    self.gate.initial_identity, demote=True
                )

        try:
            socket_namespaces = {}
            while True:
                rq = self.stream.recv()
                
                if not rq:
                    return

                if rq.object['type'] == 'http':
                    gevent.spawn(self.handle_http_request, rq)

                if rq.object['type'] == 'socket':
                    msg = rq.object['message']
                    nsid = rq.object['namespace']
                    event = rq.object['event']

                    if event == 'connect':
                        socket_namespaces[nsid] = WorkerSocketNamespace(
                            self.context, nsid
                        )

                    socket_namespaces[nsid].process_event(event, msg)

                    if event == 'disconnect':
                        socket_namespaces[nsid].destroy()
                        logging.debug('Socket disconnected, destroying endpoints')

                if rq.object['type'] == 'config-data':
                    logging.debug('Received a config update')
                    aj.config.data = rq.object['data']
                    self._master_config_reloaded.set()

        # pylint: disable=W0703
        except Exception:
            logging.error('Worker crashed!')
            traceback.print_exc()
Beispiel #9
0
 def handle_api_identity(self, http_context):
     return {
         'identity': {
             'user': AuthenticationService.get(self.context).get_identity(),
             'uid': os.getuid(),
             'effective': os.geteuid(),
             'elevation_allowed': aj.config.data['auth'].get('allow_sudo', False),
             'profile': AuthenticationService.get(self.context).get_provider().get_profile(
                 AuthenticationService.get(self.context).get_identity()
             ),
         },
         'machine': {
             'name': aj.config.data['name'],
             'hostname': socket.gethostname(),
         },
         'color': aj.config.data.get('color', None),
     }
Beispiel #10
0
 def handle_api_identity(self, http_context):
     return {
         'identity': {
             'user': AuthenticationService.get(self.context).get_identity(),
             'uid': os.getuid(),
             'effective': os.geteuid(),
             'elevation_allowed': aj.config.data['auth'].get('allow_sudo', False),
             'profile': AuthenticationService.get(self.context).get_provider().get_profile(
                 AuthenticationService.get(self.context).get_identity()
             ),
         },
         'machine': {
             'name': aj.config.data['name'],
             'hostname': socket.gethostname(),
         },
         'color': aj.config.data.get('color', None),
     }
Beispiel #11
0
 def handle_api_change_password(self, http_context):
     provider = AuthenticationService.get(self.context).get_provider()
     try:
         provider.change_password(
             self.context.identity,
             http_context.json_body()['password'],
             http_context.json_body()['new_password']
         )
     except Exception as e:
         raise EndpointError(None, str(e))
Beispiel #12
0
    def run(self):
        if self.gate.restricted:
            restricted_user = aj.config.data["restricted_user"]
            self.demote(pwd.getpwnam(restricted_user).pw_uid)
        else:
            if self.gate.initial_identity:
                AuthenticationService.get(self.context).login(self.gate.initial_identity, demote=True)

        try:
            socket_namespaces = {}
            while True:
                rq = self.stream.recv()
                if not rq:
                    return

                if rq.object["type"] == "http":
                    gevent.spawn(self.handle_http_request, rq)

                if rq.object["type"] == "socket":
                    msg = rq.object["message"]
                    nsid = rq.object["namespace"]
                    event = rq.object["event"]

                    if event == "connect":
                        socket_namespaces[nsid] = WorkerSocketNamespace(self.context, nsid)

                    socket_namespaces[nsid].process_event(event, msg)

                    if event == "disconnect":
                        socket_namespaces[nsid].destroy()
                        logging.debug("Socket disconnected, destroying endpoints")

                if rq.object["type"] == "config-data":
                    logging.debug("Received a config update")
                    aj.config.data = rq.object["data"]
                    self._master_config_reloaded.set()

        # pylint: disable=W0703
        except Exception:
            logging.error("Worker crashed!")
            traceback.print_exc()
Beispiel #13
0
 def handle_api_identity(self, http_context):
     return {
         'identity': {
             'user': AuthenticationService.get(self.context).get_identity(),
             'effective': os.geteuid(),
         },
         'machine': {
             'name': aj.config.data['name'],
             'hostname': socket.gethostname(),
         },
         'color': aj.config.data['color'],
     }
Beispiel #14
0
    def handle_api_check_password_complexity(self, http_context):
        """
        Check the password complexity requirements from the auth provider before saving a new password for the password reset functionality.

        :param http_context: HttpContext
        :type http_context: HttpContext
        :return: Error if test failed
        :rtype: basestring
        """

        password = http_context.json_body()['password']
        return AuthenticationService.get(self.context).get_provider().check_password_complexity(password)
Beispiel #15
0
    def handle_api_auth(self, http_context):
        body_data = json.loads(http_context.body)
        mode = body_data['mode']
        username = body_data.get('username', None)
        password = body_data.get('password', None)

        auth = AuthenticationService.get(self.context)

        if mode == 'normal':
            auth_info = auth.check_password(username, password)
            if auth_info:
                auth.prepare_session_redirect(http_context, username,
                                              auth_info)
                return {
                    'success': True,
                    'username': username,
                }
            else:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': None,
                }
        elif mode == 'sudo':
            target = 'root'
            try:
                if auth.check_sudo_password(username, password):
                    auth.prepare_session_redirect(http_context, target, None)
                    return {
                        'success': True,
                        'username': target,
                    }
                else:
                    gevent.sleep(3)
                    return {
                        'success': False,
                        'error': _('Authorization failed'),
                    }
            except SudoError as e:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': e.message,
                }
        return {
            'success': False,
            'error': 'Invalid mode',
        }
Beispiel #16
0
 def load(self):
     if self.user == 'root':
         self.data = yaml.load(open('/root/.config/ajenti.yml'),
                               Loader=yaml.Loader)
     else:
         ## Load ldap attribute webuidashboard
         userAttrs = AuthenticationService.get(
             self.context).get_provider()._get_ldap_user(
                 self.user, context="userconfig")
         try:
             self.data = json.loads(userAttrs['sophomorixWebuiDashboard'])
         except Exception as e:
             logging.warning(
                 'Error retrieving userconfig from %s, value: %s. This will be overwritten',
                 self.user, userAttrs['sophomorixWebuiDashboard'])
             self.data = {}
Beispiel #17
0
    def handle_api_auth(self, http_context):
        body_data = json.loads(http_context.body)
        mode = body_data['mode']
        username = body_data.get('username', None)
        password = body_data.get('password', None)

        auth = AuthenticationService.get(self.context)

        if mode == 'normal':
            auth_info = auth.check_password(username, password)
            if auth_info:
                auth.prepare_session_redirect(http_context, username, auth_info)
                return {
                    'success': True,
                    'username': username,
                }
            else:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': None,
                }
        elif mode == 'sudo':
            target = 'root'
            try:
                if auth.check_sudo_password(username, password):
                    auth.prepare_session_redirect(http_context, target, None)
                    return {
                        'success': True,
                        'username': target,
                    }
                else:
                    gevent.sleep(3)
                    return {
                        'success': False,
                        'error': _('Authorization failed'),
                    }
            except SudoError as e:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': e.message,
                }
        return {
            'success': False,
            'error': 'Invalid mode',
        }
Beispiel #18
0
    def handle_api_auth_providers(self, http_context):
        """
        Load all authentication methods.

        :param http_context: HttpContext
        :type http_context: HttpContext
        :return: List of authenticator, one per dict
        :rtype: list of dict
        """

        r = []
        auth_service_id = AuthenticationService.get(
            self.context).get_provider().id
        for p in AuthenticationProvider.all(self.context):
            r.append({
                'id': p.id,
                'name': p.name,
                'used': True if p.id == auth_service_id else False,
            })
        return r
Beispiel #19
0
 def __init__(self, context):
     self.context = context
     self.auth_provider = AuthenticationService.get(
         self.context).get_provider()
     self.notifications = Mail()
     self.ensure_secret_key()
Beispiel #20
0
    def handle_api_auth(self, http_context):
        body_data = json.loads(http_context.body)
        mode = body_data['mode']
        username = body_data.get('username', None)
        password = body_data.get('password', None)

        auth = AuthenticationService.get(self.context)

        if mode == 'normal':
            if auth.check_password(username, password):
                auth.prepare_session_redirect(http_context, username)
                return {
                    'success': True,
                    'username': username,
                }
            else:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': None,
                }
        elif mode == 'sudo':
            target = 'root'
            try:
                auth.check_sudo_password(username, password)
                auth.prepare_session_redirect(http_context, target)
                return {
                    'success': True,
                    'username': target,
                }
            except SudoError as e:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': e.message,
                }
        elif mode == 'persona':
            assertion = body_data.get('assertion', None)
            audience = body_data.get('audience', None)

            if not assertion or not audience:
                return {
                    'success': False,
                    'error': 'Invalid params',
                }

            try:
                email = auth.check_persona_assertion(assertion, audience)
            except Exception as e:
                traceback.print_exc()
                return {
                    'success':
                    False,
                    'error':
                    'Could not authenticate with Mozilla Persona: %s' % str(e),
                }

            emails = aj.config.data.get('emails', {})
            if email in emails:
                username = emails[email]
                auth.prepare_session_redirect(http_context, username)
                return {
                    'success': True,
                    'username': username,
                }
            else:
                return {
                    'success': False,
                    'error': 'Unrecognized e-mail',
                }

        return {
            'success': False,
            'error': 'Invalid mode',
        }
Beispiel #21
0
    def handle_api_auth(self, http_context):
        body_data = json.loads(http_context.body)
        mode = body_data['mode']
        username = body_data.get('username', None)
        password = body_data.get('password', None)

        auth = AuthenticationService.get(self.context)

        if mode == 'normal':
            if auth.check_password(username, password):
                auth.prepare_session_redirect(http_context, username)
                return {
                    'success': True,
                    'username': username,
                }
            else:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': None,
                }
        elif mode == 'sudo':
            target = 'root'
            try:
                if auth.check_sudo_password(username, password):
                    auth.prepare_session_redirect(http_context, target)
                    return {
                        'success': True,
                        'username': target,
                    }
                else:
                    gevent.sleep(3)
                    return {
                        'success': False,
                        'error': _('Authorization failed'),
                    }
            except SudoError as e:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': e.message,
                }
        elif mode == 'persona':
            assertion = body_data.get('assertion', None)
            audience = body_data.get('audience', None)

            if not assertion or not audience:
                return {
                    'success': False,
                    'error': 'Invalid params',
                }

            try:
                email = auth.check_persona_assertion(assertion, audience)
            except Exception as e:
                traceback.print_exc()
                return {
                    'success': False,
                    'error': _('Could not authenticate with Mozilla Persona: %s') % str(e),
                }

            emails = aj.config.data['auth']['emails']
            if email in emails:
                username = emails[email]
                auth.prepare_session_redirect(http_context, username)
                return {
                    'success': True,
                    'username': username,
                }
            else:
                return {
                    'success': False,
                    'error': _('Unrecognized e-mail'),
                }

        return {
            'success': False,
            'error': 'Invalid mode',
        }
Beispiel #22
0
def run(config=None, plugin_providers=None, product_name='ajenti', dev_mode=False,
        debug_mode=False, autologin=False):
    """
    A global entry point for Ajenti.

    :param config: config file implementation instance to use
    :type  config: :class:`aj.config.BaseConfig`
    :param plugin_providers: list of plugin providers to load plugins from
    :type  plugin_providers: list(:class:`aj.plugins.PluginProvider`)
    :param str product_name: a product name to use
    :param bool dev_mode: enables dev mode (automatic resource recompilation)
    :param bool debug_mode: enables debug mode (verbose and extra logging)
    :param bool autologin: disables authentication and logs everyone in as the user running the panel. This is EXTREMELY INSECURE.
    """
    if config is None:
        raise TypeError('`config` can\'t be None')

    reload(sys)
    sys.setdefaultencoding('utf8')

    aj.product = product_name
    aj.debug = debug_mode
    aj.dev = dev_mode
    aj.dev_autologin = autologin

    aj.init()
    aj.log.set_log_params(tag='master', master_pid=os.getpid())
    aj.context = Context()
    aj.config = config
    aj.plugin_providers = plugin_providers or []
    logging.info('Loading config from %s', aj.config)
    aj.config.load()
    aj.config.ensure_structure()

    if aj.debug:
        logging.warn('Debug mode')
    if aj.dev:
        logging.warn('Dev mode')

    try:
        locale.setlocale(locale.LC_ALL, '')
    except locale.Error:
        logging.warning('Couldn\'t set default locale')

    # install a passthrough gettext replacement since all localization is handled in frontend
    # and _() is here only for string extraction
    __builtins__['_'] = lambda x: x

    logging.info('Ajenti Core %s', aj.version)
    logging.info('Detected platform: %s / %s', aj.platform, aj.platform_string)

    # Load plugins
    PluginManager.get(aj.context).load_all_from(aj.plugin_providers)
    if len(PluginManager.get(aj.context)) == 0:
        logging.warn('No plugins were loaded!')

    if aj.config.data['bind']['mode'] == 'unix':
        path = aj.config.data['bind']['socket']
        if os.path.exists(path):
            os.unlink(path)
        listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            listener.bind(path)
        except OSError:
            logging.error('Could not bind to %s', path)
            sys.exit(1)

    if aj.config.data['bind']['mode'] == 'tcp':
        host = aj.config.data['bind']['host']
        port = aj.config.data['bind']['port']
        listener = socket.socket(
            socket.AF_INET6 if ':' in host else socket.AF_INET, socket.SOCK_STREAM
        )
        if aj.platform not in ['freebsd', 'osx']:
            try:
                listener.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1)
            except socket.error:
                logging.warn('Could not set TCP_CORK')
        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        logging.info('Binding to [%s]:%s', host, port)
        try:
            listener.bind((host, port))
        except socket.error as e:
            logging.error('Could not bind: %s', str(e))
            sys.exit(1)

    # Fix stupid socketio bug (it tries to do *args[0][0])
    socket.socket.__getitem__ = lambda x, y: None

    listener.listen(10)

    gateway = GateMiddleware.get(aj.context)
    application = HttpRoot(HttpMiddlewareAggregator([gateway])).dispatch

    aj.server = SocketIOServer(
        listener,
        log=open(os.devnull, 'w'),
        application=application,
        handler_class=RequestHandler,
        policy_server=False,
        transports=[
            str('websocket'),
            str('flashsocket'),
            str('xhr-polling'),
            str('jsonp-polling'),
        ],
    )

    if aj.config.data['ssl']['enable'] and aj.config.data['bind']['mode'] == 'tcp':
        try:
            context = SSL.Context(SSL.TLSv1_2_METHOD)
        except:
            context = SSL.Context(SSL.TLSv1_METHOD)
        context.set_session_id(str(id(context)))
        context.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
        context.set_cipher_list('ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM')

        certificate = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            open(aj.config.data['ssl']['certificate']).read()
        )
        private_key = crypto.load_privatekey(
            crypto.FILETYPE_PEM,
            open(aj.config.data['ssl']['certificate']).read()
        )

        context.use_certificate(certificate)
        context.use_privatekey(private_key)

        if aj.config.data['ssl']['client_auth']['enable']:
            # todo harden files
            logging.info('Enabling SSL client authentication')
            context.add_client_ca(certificate)
            context.get_cert_store().add_cert(certificate)
            verify_flags = SSL.VERIFY_PEER
            if aj.config.data['ssl']['client_auth']['force']:
                verify_flags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT
            context.set_verify(verify_flags, AuthenticationService.get(aj.context).client_certificate_callback)
            context.set_verify_depth(0)

        aj.server.ssl_args = {'server_side': True}
        aj.server.wrap_socket = lambda socket, **ssl: SSLSocket(context, socket)
        logging.info('SSL enabled')

    # auth.log
    try:
        syslog.openlog(
            ident=str(aj.product),
            facility=syslog.LOG_AUTH,
        )
    except:
        syslog.openlog(aj.product)

    def cleanup():
        if hasattr(cleanup, 'started'):
            return
        cleanup.started = True
        logging.info('Process %s exiting normally', os.getpid())
        gevent.signal(signal.SIGINT, lambda: None)
        gevent.signal(signal.SIGTERM, lambda: None)
        if aj.master:
            gateway.destroy()

        p = psutil.Process(os.getpid())
        for c in p.children(recursive=True):
            try:
                os.killpg(c.pid, signal.SIGTERM)
                os.killpg(c.pid, signal.SIGKILL)
            except OSError:
                pass

    def signal_handler():
        cleanup()
        sys.exit(0)

    gevent.signal(signal.SIGINT, signal_handler)
    gevent.signal(signal.SIGTERM, signal_handler)

    aj.server.serve_forever()

    if not aj.master:
        # child process, server is stopped, wait until killed
        gevent.wait()

    if hasattr(aj.server, 'restart_marker'):
        logging.warn('Restarting by request')
        cleanup()

        fd = 20  # Close all descriptors. Creepy thing
        while fd > 2:
            try:
                os.close(fd)
                logging.debug('Closed descriptor #%i', fd)
            except OSError:
                pass
            fd -= 1

        logging.warn('Will restart the process now')
        if '-d' in sys.argv:
            sys.argv.remove('-d')
        os.execv(sys.argv[0], sys.argv)
    else:
        if aj.master:
            logging.debug('Server stopped')
            cleanup()
Beispiel #23
0
    def handle_api_auth(self, http_context):
        """
        Test user authentication to login or to elevate privileges.

        :param http_context: HttpContext
        :type http_context: HttpContext
        :return: Success status and username
        :rtype: dict
        """

        body_data = json.loads(http_context.body.decode())
        mode = body_data['mode']
        username = body_data.get('username', None)
        password = body_data.get('password', None)

        auth = AuthenticationService.get(self.context)

        if mode == 'normal':
            auth_info = auth.check_password(username, password)
            if auth_info:
                auth.prepare_session_redirect(http_context, username, auth_info)
                return {
                    'success': True,
                    'username': username,
                }

            # Log failed login for e.g. fail2ban
            remote_addr = http_context.env.get('REMOTE_ADDR', None)
            if len(aj.config.data['trusted_proxies']) > 0:
                if remote_addr in aj.config.data['trusted_proxies']:
                    ip = http_context.env.get('HTTP_X_FORWARDED_FOR', '').split(',')[0]
            else:
                ip = remote_addr
            logging.warning(f"Failed login from {username} at IP : {ip}")

            gevent.sleep(3)
            return {
                'success': False,
                'error': None,
            }

        elif mode == 'sudo':
            target = 'root'
            try:
                if auth.check_sudo_password(username, password):
                    self.context.worker.terminate()
                    auth.prepare_session_redirect(http_context, target, None)
                    return {
                        'success': True,
                        'username': target,
                    }

                gevent.sleep(3)
                return {
                    'success': False,
                    'error': _('Authorization failed'),
                }
            except SudoError as e:
                gevent.sleep(3)
                return {
                    'success': False,
                    'error': e.message,
                }
        return {
            'success': False,
            'error': 'Invalid mode',
        }
Beispiel #24
0
def run(config=None, plugin_providers=None, product_name='ajenti', dev_mode=False,
        debug_mode=False, autologin=False):
    """
    A global entry point for Ajenti.

    :param config: config file implementation instance to use
    :type  config: :class:`aj.config.BaseConfig`
    :param plugin_providers: list of plugin providers to load plugins from
    :type  plugin_providers: list(:class:`aj.plugins.PluginProvider`)
    :param product_name: a product name to use
    :param dev_mode: enables dev mode (automatic resource recompilation)
    :type  dev_mode: bool
    :param debug_mode: enables debug mode (verbose and extra logging)
    :type  debug_mode: bool
    :param autologin: disables authentication and logs everyone in as the user running the panel. This is EXTREMELY INSECURE.
    :type  autologin: bool
    """
    if config is None:
        raise TypeError('`config` can\'t be None')

    reload(sys)
    sys.setdefaultencoding('utf8')

    aj.product = product_name
    aj.debug = debug_mode
    aj.dev = dev_mode
    aj.dev_autologin = autologin

    aj.init()
    aj.log.set_log_params(tag='master', master_pid=os.getpid())
    aj.context = Context()
    aj.config = config
    aj.plugin_providers = plugin_providers or []
    logging.info('Loading config from %s', aj.config)
    aj.config.load()

    if aj.debug:
        logging.warn('Debug mode')
    if aj.dev:
        logging.warn('Dev mode')

    try:
        locale.setlocale(locale.LC_ALL, '')
    except locale.Error:
        logging.warning('Couldn\'t set default locale')

    logging.info('Ajenti Core %s', aj.version)
    logging.info('Detected platform: %s / %s', aj.platform, aj.platform_string)

    # Load plugins
    PluginManager.get(aj.context).load_all_from(aj.plugin_providers)
    if len(PluginManager.get(aj.context).get_all()) == 0:
        logging.warn('No plugins were loaded!')

    if aj.config.data['bind']['mode'] == 'unix':
        path = aj.config.data['bind']['socket']
        if os.path.exists(path):
            os.unlink(path)
        listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            listener.bind(path)
        except OSError:
            logging.error('Could not bind to %s', path)
            sys.exit(1)

    if aj.config.data['bind']['mode'] == 'tcp':
        host = aj.config.data['bind']['host']
        port = aj.config.data['bind']['port']
        listener = socket.socket(
            socket.AF_INET6 if ':' in host else socket.AF_INET, socket.SOCK_STREAM
        )
        if aj.platform not in ['freebsd', 'osx']:
            try:
                listener.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1)
            except socket.error:
                logging.warn('Could not set TCP_CORK')
        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        logging.info('Binding to [%s]:%s', host, port)
        try:
            listener.bind((host, port))
        except socket.error as e:
            logging.error('Could not bind: %s', str(e))
            sys.exit(1)

    # Fix stupid socketio bug (it tries to do *args[0][0])
    socket.socket.__getitem__ = lambda x, y: None

    listener.listen(10)

    gateway = GateMiddleware.get(aj.context)
    application = HttpRoot(HttpMiddlewareAggregator([gateway])).dispatch

    aj.server = SocketIOServer(
        listener,
        log=open(os.devnull, 'w'),
        application=application,
        handler_class=RequestHandler,
        policy_server=False,
        transports=[
            str('websocket'),
            str('flashsocket'),
            str('xhr-polling'),
            str('jsonp-polling'),
        ],
    )

    if aj.config.data['ssl']['enable'] and aj.config.data['bind']['mode'] == 'tcp':
        try:
            context = SSL.Context(SSL.TLSv1_2_METHOD)
        except:
            context = SSL.Context(SSL.TLSv1_METHOD)
        context.set_session_id(str(id(context)))
        context.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
        context.set_cipher_list('ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM')

        certificate = crypto.load_certificate(
            crypto.FILETYPE_PEM,
            open(aj.config.data['ssl']['certificate']).read()
        )
        private_key = crypto.load_privatekey(
            crypto.FILETYPE_PEM,
            open(aj.config.data['ssl']['certificate']).read()
        )

        context.use_certificate(certificate)
        context.use_privatekey(private_key)

        if aj.config.data['ssl']['client_auth']['enable']:
            # todo harden files
            logging.info('Enabling SSL client authentication')
            context.add_client_ca(certificate)
            context.get_cert_store().add_cert(certificate)
            verify_flags = SSL.VERIFY_PEER
            if aj.config.data['ssl']['client_auth']['force']:
                verify_flags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT
            context.set_verify(verify_flags, AuthenticationService.get(aj.context).client_certificate_callback)
            context.set_verify_depth(0)

        aj.server.ssl_args = {'server_side': True}
        aj.server.wrap_socket = lambda socket, **ssl: SSLSocket(context, socket)
        logging.info('SSL enabled')

    # auth.log
    try:
        syslog.openlog(
            ident=str(aj.product),
            facility=syslog.LOG_AUTH,
        )
    except:
        syslog.openlog(aj.product)

    def cleanup():
        if hasattr(cleanup, 'started'):
            return
        cleanup.started = True
        logging.info('Process %s exiting normally', os.getpid())
        gevent.signal(signal.SIGINT, lambda: None)
        gevent.signal(signal.SIGTERM, lambda: None)
        if aj.master:
            gateway.destroy()

        p = psutil.Process(os.getpid())
        for c in p.children(recursive=True):
            try:
                os.killpg(c.pid, signal.SIGTERM)
                os.killpg(c.pid, signal.SIGKILL)
            except OSError:
                pass

    def signal_handler():
        cleanup()
        sys.exit(0)

    gevent.signal(signal.SIGINT, signal_handler)
    gevent.signal(signal.SIGTERM, signal_handler)

    aj.server.serve_forever()

    if not aj.master:
        # child process, server is stopped, wait until killed
        gevent.wait()

    if hasattr(aj.server, 'restart_marker'):
        logging.warn('Restarting by request')
        cleanup()

        fd = 20  # Close all descriptors. Creepy thing
        while fd > 2:
            try:
                os.close(fd)
                logging.debug('Closed descriptor #%i', fd)
            except OSError:
                pass
            fd -= 1

        logging.warn('Will restart the process now')
        if '-d' in sys.argv:
            sys.argv.remove('-d')
        os.execv(sys.argv[0], sys.argv)
    else:
        if aj.master:
            logging.debug('Server stopped')
            cleanup()