403 Forbidden


Disable Functions:
Path : /usr/libexec/kcare/python/kcarectl/
File Upload :
Command :
Current File : //usr/libexec/kcare/python/kcarectl/auth.py

# Copyright (c) Cloud Linux Software, Inc
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT

import base64
import os
import time

from . import errors
from . import config
from . import constants
from . import http_utils
from . import log_utils
from . import platform_utils
from . import utils
from .py23 import urlencode, json_loads_nstr, URLError, HTTPError


SYSTEMID = '/etc/sysconfig/kcare/systemid'
ALMA_SYSTEMID = '/etc/sysconfig/kcare/systemid.almacare'
IM360_LICENSE_FILE = '/var/imunify360/license.json'


def _systemid():
    if not os.path.exists(SYSTEMID):
        return None

    with open(SYSTEMID, 'r') as fd:
        for line in fd:
            param, _, value = line.partition('=')
            if param.strip() == 'server_id':
                return value.strip()
            raise errors.KcareError('Unable to parse {0}.'.format(SYSTEMID))


def _alma_systemid():
    if not os.path.exists(ALMA_SYSTEMID):
        return None
    with open(ALMA_SYSTEMID, 'r') as f:
        return f.readline().strip()


def _im360_systemid():
    if not os.path.exists(IM360_LICENSE_FILE):
        return None

    data = {}
    with open(IM360_LICENSE_FILE) as f:
        content = f.read()
        if content:
            try:
                data = json_loads_nstr(content)
            except Exception:
                pass  # we are not interested why lic file can't be parsed
    return data.get('id')


@utils.cached
def get_serverid():
    """Get server_id or None if not present.

    Lookup order: SYSTEMID then IM360_LICENSE_FILE then ALMA_SYSTEMID
    """
    return _systemid() or _im360_systemid() or _alma_systemid()


def _rm_serverid():
    os.unlink(SYSTEMID)


def _set_server_id(server_id):
    utils.atomic_write(SYSTEMID, 'server_id={0}\n'.format(server_id))


def unregister(silent=False):
    url = None
    try:
        server_id = get_serverid()
        if server_id is None:
            if not silent:
                log_utils.logerror('Error unregistering server: cannot find server id')
            return
        url = config.REGISTRATION_URL + '/unregister_server.plain?server_id={0}'.format(server_id)
        response = http_utils.urlopen(url)
        content = utils.nstr(response.read())
        res = utils.data_as_dict(content)
        if res['success'] == 'true':
            _rm_serverid()
            if not silent:
                log_utils.loginfo('Server was unregistered')
        elif not silent:
            log_utils.logerror(content)
            log_utils.logerror('Error unregistering server: ' + res['message'])
    except HTTPError as e:
        if not silent:
            log_utils.print_cln_http_error(e, url)


def _register_retry(url):  # pragma: no cover unit
    print('Register auto-retry has been enabled, the system can be registered later')
    pid = os.fork()
    if pid > 0:
        return
    os.setsid()
    pid = os.fork()
    import sys

    if pid > 0:
        sys.exit(0)
    sys.stdout.flush()
    si = open('/dev/null', 'r')
    so = open('/dev/null', 'a+')
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(so.fileno(), sys.stderr.fileno())
    while True:
        time.sleep(60 * 60 * 2)
        code, server_id, auth_token = _try_register(url)
        if code == 0 and server_id:
            _set_server_id(server_id)
            _set_auth_token(auth_token)
            sys.exit(0)


def _try_register(url):
    try:
        response = http_utils.urlopen(url)
        auth_token = response.headers.get(constants.AUTH_TOKEN_HEADER, None)
        res = utils.data_as_dict(utils.nstr(response.read()))
        return int(res['code']), res.get('server_id'), auth_token
    except (HTTPError, URLError) as e:
        log_utils.print_cln_http_error(e, url)
        return None, None, None
    except Exception:
        log_utils.kcarelog.exception('Exception while trying to register URL %s' % url)
        return None, None, None


def register(key, retry=False):
    try:
        unregister(True)
    except Exception:
        log_utils.kcarelog.exception('Exception while trying to unregister URL before register.')

    hostname = platform_utils.get_hostname()

    query = urlencode([('hostname', hostname), ('key', key)])
    url = '{0}/register_server.plain?{1}'.format(config.REGISTRATION_URL, query)

    code, server_id, auth_token = _try_register(url)
    if code == 0:
        _set_server_id(server_id)
        _set_auth_token(auth_token)
        log_utils.loginfo('Server Registered')
        return 0
    elif code == 1:
        log_utils.logerror('Account Locked')
    elif code == 2:
        log_utils.logerror('Invalid Key')
    elif code == 3:
        log_utils.logerror(
            'You have reached maximum registered servers for this key. '
            'Please go to your CLN account, remove unused servers and try again.'
        )
    elif code == 4:
        log_utils.logerror('IP is not allowed. Please change allowed IP ranges for the key in KernelCare Key tab in CLN')
    elif code == 5:
        log_utils.logerror('This IP was already used for trial, you cannot use it for trial again')
    elif code == 6:
        log_utils.logerror('This IP was banned. Please contact support for more information at https://www.kernelcare.com/support/')
    else:
        log_utils.logerror('Unknown Error {0}'.format(code))
    if retry:  # pragma: no cover
        _register_retry(url)
        return 0
    return code or -1


@utils.cached
def _get_auth_token():
    return utils.try_to_read(constants.AUTH_TOKEN_DUMP_PATH)


def _set_auth_token(auth_token):
    if not auth_token:
        return

    utils.atomic_write(constants.AUTH_TOKEN_DUMP_PATH, auth_token)


def urlopen_auth(url, *args, **kwargs):
    method = kwargs.pop('method', None)
    if kwargs.pop('check_license', True):
        check = _check_auth_retry
    else:
        check = http_utils.check_urlopen_retry
    if http_utils.is_local_url(url):
        return http_utils.urlopen_base(url, *args, **kwargs)

    request = http_utils.http_request(url, get_http_auth_string(), _get_auth_token(), method=method)
    return utils.retry(check, count=8)(http_utils.urlopen_base)(request, *args, **kwargs)


def get_http_auth_string():
    server_id = get_serverid()
    if server_id:
        return utils.nstr(base64.b64encode(utils.bstr('{0}:{1}'.format(server_id, 'kernelcare'))))
    return None


def _check_auth_retry(e, state):
    if isinstance(e, HTTPError):
        if e.code in (403, 401):
            return _handle_forbidden(state)
        return e.code >= 500
    elif isinstance(e, URLError):
        return True


def _handle_forbidden(state):
    """In case of 403 error we should check what's happen.
    Case #1. We are trying to register unlicensed machine and should try to register trial.
    Case #2. We have a valid license but access restrictions on server are not consistent yet
             and we had to try later.
    """
    if 'license' in state:
        # license has already been checked and is valid, no need to ask CLN again
        return True

    if config.CHECK_CLN_LICENSE_STATUS:
        server_id = get_serverid()
        if server_id:
            url = config.REGISTRATION_URL + '/check.plain' + '?server_id={0}'.format(server_id)
        else:
            url = config.REGISTRATION_URL + '/check.plain'

        try:
            # do not retry in case of 500 from CLN!
            # otherwise, CLN will die in pain because of too many requests
            content = utils.nstr(http_utils.urlopen_base(url).read())
            info = utils.data_as_dict(content)
        except URLError as ex:
            log_utils.print_cln_http_error(ex, url, stdout=False)
            return

        if not info or not info.get('code'):
            log_utils.kcarelog.error('Unexpected CLN response: {0}'.format(content))
            return

        if info['code'] in ['0', '1']:
            # license is fine: 0 - valid license, 1 - valid trial license;
            # looks like htpasswd not updated yet;
            # mark state as licensed to avoid repeated requests to CLN
            state['license'] = True
            log_utils.logerror('Unable to access server. Retrying...')
            return True
        else:
            _register_trial()


def license_info():
    server_id = get_serverid()
    if server_id:
        url = config.REGISTRATION_URL + '/check.plain?server_id={0}'.format(server_id)
        try:
            response = http_utils.urlopen(url)
            content = utils.nstr(response.read())
            res = utils.data_as_dict(content)
            if not res or not res.get('code'):
                print('Unexpected CLN response: {0}'.format(content))
                return 1
            code = int(res['code'])
            if code == 0:
                print('Key-based valid license found')
                return 1
            else:
                license_type = _get_license_info_by_ip(key_checked=1)
                if license_type == 0:
                    print('No valid key-based license found')
                return license_type
        except HTTPError as e:
            log_utils.print_cln_http_error(e, url)
            return 0
    else:
        return _get_license_info_by_ip()


def _get_license_info_by_ip(key_checked=0):
    url = config.REGISTRATION_URL + '/check.plain'
    try:
        response = http_utils.urlopen(url)
        content = utils.nstr(response.read())
        res = utils.data_as_dict(content)
        if res['success'].lower() == 'true':
            code = int(res['code'])
            if code == 0:
                print('Valid license found for IP {0}'.format(res['ip']))
                return 1  # valid license
            if code == 1:
                ip = res['ip']
                expires_str = utils.parse_response_date(res['expire_date']).strftime('%Y-%m-%d')
                print('You have a trial license for the IP {0} that will expire on {1}'.format(ip, expires_str))
                return 2  # trial license
            if code == 2 and key_checked == 0:
                ip = res['ip']
                expires_str = utils.parse_response_date(res['expire_date']).strftime('%Y-%m-%d')
                print('Your trial license for the IP {0} expired on {1}'.format(ip, expires_str))
            if code == 3 and key_checked == 0:
                if 'ip' in res:
                    print("The IP {0} hasn't been licensed".format(res['ip']))
                else:
                    print("This server hasn't been licensed")
        else:
            message = res.get('message', '')
            print('Error retrieving license info: {0}'.format(message))
    except HTTPError as e:
        log_utils.print_cln_http_error(e, url)
    except KeyError as key:
        print('Unexpected CLN response, cannot find {0} key:\n{1}'.format(key, content.strip()))
    return 0  # no valid license


def _register_trial():
    trial_mark = os.path.join(constants.PATCH_CACHE, 'trial-requested')
    if os.path.exists(trial_mark):
        return

    try:
        response = http_utils.urlopen(config.REGISTRATION_URL + '/trial.plain')
        res = utils.data_as_dict(utils.nstr(response.read()))
        try:
            if res['success'].lower() == 'true':
                utils.atomic_write(trial_mark, '', ensure_dir=True)
                if res['expired'] == 'true':
                    raise errors.AlreadyTrialedException(res['ip'], res['created'])
                log_utils.loginfo('Requesting trial license for IP {0}. Please wait...'.format(res['ip']))
                return None
            elif res['success'] == 'na':
                utils.atomic_write(trial_mark, '', ensure_dir=True)
                raise errors.KcareError('Invalid License')
            else:
                # TODO: make sane exception messages
                raise errors.UnableToGetLicenseException(-1)  # Invalid response?
        except KeyError as ke:
            raise errors.UnableToGetLicenseException(ke)
    except HTTPError as e:
        raise errors.UnableToGetLicenseException(e.code)

404 Not Found
[ LogOut ]