Exemple #1
0
def check_version_status(lint=False):
    """Check our version string in DNS for known issues and warn about them

    the lookup should be <7 chars of commitid>.<patch>.<minor>.<major>.versioncheck.fuglu.org
    in case of a release version, use 'release' instead of commit id

    eg, the lookup for 0.6.3 would be:
    release.3.6.0.versioncheck.fuglu.org

    DNS will return NXDOMAIN or 127.0.0.<bitmask>
    2: generic non security related issue
    4: low risk security issue
    8: high risk security issue
    """
    bitmaskmap = {
        2:
        "there is a known (not security related) issue with this version - consider upgrading",
        4:
        "there is a known low-risk security issue with this version - an upgrade is recommended",
        8:
        "there is a known high-risk security issue with this version - upgrade as soon as possible!",
    }

    m = re.match(
        r'^(?P<major>\d{1,4})\.(?P<minor>\d{1,4})\.(?P<patch>\d{1,4})(?:\-(?P<commitno>\d{1,4})\-g(?P<commitid>[a-f0-9]{7}))?$',
        FUGLU_VERSION)
    if m is None:
        logging.warn("could not parse my version string %s" % FUGLU_VERSION)
        return
    parts = m.groupdict()
    if 'commitid' not in parts or parts['commitid'] is None:
        parts['commitid'] = 'release'

    lookup = "{commitid}.{patch}.{minor}.{major}.versioncheck.fuglu.org".format(
        **parts)
    result = None
    try:
        result = socket.gethostbyname(lookup)
    except Exception:
        # DNS fails happen - try again next time
        pass

    if result is None:
        return

    ret = re.match(r'^127\.0\.0\.(?P<replycode>\d{1,4})$', result)
    if ret is not None:
        code = int(ret.groupdict()['replycode'])
        for bitmask, message in bitmaskmap.items():
            if code & bitmask == bitmask:
                logging.warn(message)
                if lint:
                    fc = FunkyConsole()
                    print(fc.strcolor(message, "yellow"))
Exemple #2
0
def check_version_status(lint=False):
    """Check our version string in DNS for known issues and warn about them

    the lookup should be <7 chars of commitid>.<patch>.<minor>.<major>.versioncheck.fuglu.org
    in case of a release version, use 'release' instead of commit id

    eg, the lookup for 0.6.3 would be:
    release.3.6.0.versioncheck.fuglu.org

    DNS will return NXDOMAIN or 127.0.0.<bitmask>
    2: generic non security related issue
    4: low risk security issue
    8: high risk security issue
    """
    bitmaskmap = {
        2: "there is a known (not security related) issue with this version - consider upgrading",
        4: "there is a known low-risk security issue with this version - an upgrade is recommended",
        8: "there is a known high-risk security issue with this version - upgrade as soon as possible!",
    }

    m = re.match(
        r"^(?P<major>\d{1,4})\.(?P<minor>\d{1,4})\.(?P<patch>\d{1,4})(?:\-(?P<commitno>\d{1,4})\-g(?P<commitid>[a-f0-9]{7}))?$",
        FUGLU_VERSION,
    )
    if m == None:
        logging.warn("could not parse my version string %s" % FUGLU_VERSION)
        return
    parts = m.groupdict()
    if "commitid" not in parts or parts["commitid"] == None:
        parts["commitid"] = "release"

    lookup = "{commitid}.{patch}.{minor}.{major}.versioncheck.fuglu.org".format(**parts)
    result = None
    try:
        result = socket.gethostbyname(lookup)
    except:
        # DNS fails happen - try again next time
        pass

    if result == None:
        return

    ret = re.match(r"^127\.0\.0\.(?P<replycode>\d{1,4})$", result)
    if ret != None:
        code = int(ret.groupdict()["replycode"])
        for bitmask, message in bitmaskmap.items():
            if code & bitmask == bitmask:
                logging.warn(message)
                if lint:
                    fc = FunkyConsole()
                    print(fc.strcolor(message, "yellow"))
Exemple #3
0
    def checkConfig(self):
        """Check if all required options without default are in the config file
        Fill missing values with defaults if possible
        """
        all_ok = True
        fc = FunkyConsole()
        for config, infodic in self.requiredvars.items():
            section = infodic['section']
            try:
                var = self.config.get(section, config)

                if 'validator' in infodic and not infodic["validator"](var):
                    print("Validation failed for [%s] :: %s" % (section, config))
                    all_ok = False

            except configparser.NoSectionError:
                print(fc.strcolor(f"Missing configuration section containing variables without default "
                                  f"value [{section}] :: {config}", "red"))
                all_ok = False
            except configparser.NoOptionError:
                print(fc.strcolor(f"Missing configuration value without default [{section}] :: {config}", "red"))
                all_ok = False
                
        # missing sections -> this is only a warning since section is not required
        # as long as there are no required variables without default values...
        if all_ok:
            missingsections = set()
            for config, infodic in self.requiredvars.items():
                section = infodic['section']
                if section not in missingsections and not self.config.has_section(section):
                    missingsections.add(section)

            for section in missingsections:
                print(fc.strcolor(f"Missing configuration section [{section}] :: "
                              f"All variables will use default values", "yellow"))
        return all_ok
Exemple #4
0
    def lint(self):
        errors = 0
        fc = FunkyConsole()
        self._lint_dependencies(fc)

        print(fc.strcolor('Loading extensions...', 'magenta'))
        exts = self.load_extensions()
        for ext in exts:
            (name, enabled, status) = ext
            pname = fc.strcolor(name, 'cyan')
            if enabled:
                penabled = fc.strcolor('enabled', 'green')
            else:
                penabled = fc.strcolor('disabled', 'red')
            print("%s: %s (%s)" % (pname, penabled, status))

        print(fc.strcolor('Loading plugins...', 'magenta'))
        if not self.load_plugins():
            print(fc.strcolor('At least one plugin failed to load', 'red'))
        print(fc.strcolor('Plugin loading complete', 'magenta'))

        print("Linting ", fc.strcolor("main configuration", 'cyan'))
        if not self.checkConfig():
            print(fc.strcolor("ERROR", "red"))
        else:
            print(fc.strcolor("OK", "green"))

        trashdir = self.config.get('main', 'trashdir').strip()
        if trashdir != "":
            if not os.path.isdir(trashdir):
                print(
                    fc.strcolor("Trashdir %s does not exist" % trashdir, 'red'))

        # sql config override
        sqlconfigdbconnectstring = self.config.get(
            'databaseconfig', 'dbconnectstring')
        if sqlconfigdbconnectstring.strip() != '':
            print("")
            print("Linting ", fc.strcolor("sql configuration", 'cyan'))
            try:
                from fuglu.extensions.sql import get_session
                sess = get_session(sqlconfigdbconnectstring)
                tempsuspect = Suspect(
                    '*****@*****.**', '*****@*****.**', '/dev/null')
                sqlvars = dict(
                    section='testsection', option='testoption', scope='$GLOBAL')
                default_template_values(tempsuspect, sqlvars)
                sess.execute(self.config.get('databaseconfig', 'sql'), sqlvars)
                sess.remove()
                print(fc.strcolor("OK", 'green'))
            except Exception as e:
                print(fc.strcolor("Failed %s" % str(e), 'red'))

        allplugins = self.plugins + self.prependers + self.appenders

        for plugin in allplugins:
            print()
            print("Linting Plugin ", fc.strcolor(str(plugin), 'cyan'),
                  'Config section:', fc.strcolor(str(plugin.section), 'cyan'))
            try:
                result = plugin.lint()
            except Exception as e:
                CrashStore.store_exception()
                print("ERROR: %s" % e)
                result = False

            if result:
                print(fc.strcolor("OK", "green"))
            else:
                errors = errors + 1
                print(fc.strcolor("ERROR", "red"))
        print("%s plugins reported errors." % errors)

        if self.config.getboolean('main', 'versioncheck'):
            check_version_status(lint=True)
Exemple #5
0
newsections = newconfig.sections()

# values that usually differ from the default
excludelist = [
    ('main', 'plugins'),
    ('main', 'prependers'),
    ('main', 'identifier'),
    ('main', 'appenders'),
    ('main', 'trashdir'),
    ('ArchivePlugin', 'archivedir'),
]

for newsection in newsections:
    # print "Checking section %s"%newsection
    if not currentconfig.has_section(newsection):
        print "%s: section '%s' is missing in your current config" % (fc.strcolor('MISSING SECTION', 'red'), fc.strcolor(newsection, 'cyan'))
        continue

    newitems = newconfig.options(newsection)
    currentitems = currentconfig.options(newsection)

    toomanyitems = set(currentitems) - set(newitems)
    if len(toomanyitems) > 0:
        for item in toomanyitems:
            print "%s: Your option '%s' in section '%s' is not known in new config" % (fc.strcolor('UNKNOWN OPTION', 'yellow'), fc.strcolor(item, 'cyan'), fc.strcolor(newsection, 'cyan'))

    for key in newitems:
        defaultvalue = newconfig.get(newsection, key)
        if not currentconfig.has_option(newsection, key):
            print "%s: add option '%s' in section '%s'. Default value is: '%s'" % (fc.strcolor('MISSING OPTION', 'red'), fc.strcolor(key, 'cyan'), fc.strcolor(newsection, 'cyan'), fc.strcolor(defaultvalue, 'cyan'))
            continue
Exemple #6
0
# values that usually differ from the default
excludelist = [
    ('main', 'plugins'),
    ('main', 'prependers'),
    ('main', 'identifier'),
    ('main', 'appenders'),
    ('main', 'trashdir'),
    ('ArchivePlugin', 'archivedir'),
]

for newsection in newsections:
    # print "Checking section %s"%newsection
    if not currentconfig.has_section(newsection):
        print("%s: section '%s' is missing in your current config" %
              (fc.strcolor('MISSING SECTION',
                           'red'), fc.strcolor(newsection, 'cyan')))
        continue

    newitems = newconfig.options(newsection)
    currentitems = currentconfig.options(newsection)

    toomanyitems = set(currentitems) - set(newitems)
    if len(toomanyitems) > 0:
        for item in toomanyitems:
            print(
                "%s: Your option '%s' in section '%s' is not known in new config"
                % (fc.strcolor('UNKNOWN OPTION', 'yellow'),
                   fc.strcolor(item, 'cyan'), fc.strcolor(newsection, 'cyan')))

    for key in newitems:
        defaultvalue = newconfig.get(newsection, key)
Exemple #7
0
# values that usually differ from the default
excludelist = [
    ('main', 'plugins'),
    ('main', 'prependers'),
    ('main', 'identifier'),
    ('main', 'appenders'),
    ('main', 'trashdir'),
    ('ArchivePlugin', 'archivedir'),
]

for newsection in newsections:
    # print "Checking section %s"%newsection
    if not currentconfig.has_section(newsection):
        print("%s: section '%s' is missing in your current config" % (
            fc.strcolor('MISSING SECTION', 'red'), fc.strcolor(newsection, 'cyan')))
        continue

    newitems = newconfig.options(newsection)
    currentitems = currentconfig.options(newsection)

    toomanyitems = set(currentitems) - set(newitems)
    if len(toomanyitems) > 0:
        for item in toomanyitems:
            print("%s: Your option '%s' in section '%s' is not known in new config" % (fc.strcolor(
                'UNKNOWN OPTION', 'yellow'), fc.strcolor(item, 'cyan'), fc.strcolor(newsection, 'cyan')))

    for key in newitems:
        defaultvalue = newconfig.get(newsection, key)
        if not currentconfig.has_option(newsection, key):
            print("%s: add option '%s' in section '%s'. Default value is: '%s'" % (fc.strcolor('MISSING OPTION',
Exemple #8
0
    def lint(self):
        errors = 0
        fc = FunkyConsole()
        self._lint_dependencies(fc)

        print(fc.strcolor('Loading extensions...', 'magenta'))
        exts = self.load_extensions()
        for ext in exts:
            (name, enabled, status) = ext
            pname = fc.strcolor(name, 'cyan')
            if enabled:
                penabled = fc.strcolor('enabled', 'green')
            else:
                penabled = fc.strcolor('disabled', 'red')
            print("%s: %s (%s)" % (pname, penabled, status))

        print(fc.strcolor('Loading plugins...', 'magenta'))
        if not self.load_plugins():
            print(fc.strcolor('At least one plugin failed to load', 'red'))
        print(fc.strcolor('Plugin loading complete', 'magenta'))

        print("Linting ", fc.strcolor("main configuration", 'cyan'))
        if not self.checkConfig():
            print(fc.strcolor("ERROR", "red"))
        else:
            print(fc.strcolor("OK", "green"))

        trashdir = self.config.get('main', 'trashdir').strip()
        if trashdir != "" and not os.path.isdir(trashdir):
            print(fc.strcolor("Trashdir %s does not exist" % trashdir, 'red'))

        # sql config override
        sqlconfigdbconnectstring = self.config.get('databaseconfig',
                                                   'dbconnectstring')
        if sqlconfigdbconnectstring.strip() != '':
            print()
            print("Linting ", fc.strcolor("sql configuration", 'cyan'))
            try:
                from fuglu.extensions.sql import get_session
                sess = get_session(sqlconfigdbconnectstring)
                tempsuspect = Suspect('*****@*****.**',
                                      '*****@*****.**', '/dev/null')
                sqlvars = dict(section='testsection',
                               option='testoption',
                               scope='$GLOBAL')
                default_template_values(tempsuspect, sqlvars)
                sess.execute(self.config.get('databaseconfig', 'sql'), sqlvars)
                sess.remove()
                print(fc.strcolor("OK", 'green'))
            except Exception as e:
                print(fc.strcolor("Failed %s" % str(e), 'red'))

        allplugins = self.plugins + self.prependers + self.appenders

        for plugin in allplugins:
            print()
            print("Linting Plugin ", fc.strcolor(str(plugin), 'cyan'),
                  'Config section:', fc.strcolor(str(plugin.section), 'cyan'))
            try:
                result = plugin.lint()
            except Exception as e:
                CrashStore.store_exception()
                print("ERROR: %s" % e)
                result = False

            if result:
                print(fc.strcolor("OK", "green"))
            else:
                errors = errors + 1
                print(fc.strcolor("ERROR", "red"))
        print("%s plugins reported errors." % errors)

        if self.config.getboolean('main', 'versioncheck'):
            check_version_status(lint=True)
Exemple #9
0
    def lint(self):
        errors = 0
        fc = FunkyConsole()
        self._lint_dependencies(fc)

        print(fc.strcolor('Loading extensions...', 'magenta'))
        exts = self.load_extensions()
        for ext in exts:
            (name, enabled, status) = ext
            pname = fc.strcolor(name, 'cyan')
            if enabled:
                penabled = fc.strcolor('enabled', 'green')
            else:
                penabled = fc.strcolor('disabled', 'red')
            print("%s: %s (%s)" % (pname, penabled, status))

        print(fc.strcolor('Loading plugins...', 'magenta'))
        if not self.load_plugins():
            print(fc.strcolor('At least one plugin failed to load', 'red'))
            errors +=1
        print(fc.strcolor('Plugin loading complete', 'magenta'))

        print("Linting ", fc.strcolor("main configuration", 'cyan'))
        if not self.checkConfig():
            print(fc.strcolor("ERROR", "red"))
            errors += 1
        else:
            print(fc.strcolor("OK", "green"))

        trashdir = self.config.get('main', 'trashdir').strip()
        if trashdir != "" and not os.path.isdir(trashdir):
            print(fc.strcolor("Trashdir %s does not exist" % trashdir, 'red'))
            errors += 1

        # sql config override
        sqlconfigdbconnectstring = self.config.get('databaseconfig', 'dbconnectstring')
        if sqlconfigdbconnectstring.strip() != '':
            print()
            print("Linting ", fc.strcolor("sql configuration", 'cyan'))
            try:
                from fuglu.extensions.sql import get_session
                sess = get_session(sqlconfigdbconnectstring)
                tempsuspect = Suspect(
                    '*****@*****.**', '*****@*****.**', '/dev/null',
                    att_cachelimit=self.config.getint('performance','att_mgr_cachesize'))
                sqlvars = dict(
                    section='testsection', option='testoption', scope='$GLOBAL')
                default_template_values(tempsuspect, sqlvars)
                sess.execute(self.config.get('databaseconfig', 'sql'), sqlvars)
                sess.remove()
                print(fc.strcolor("OK", 'green'))
            except Exception as e:
                print(fc.strcolor("Failed %s" % str(e), 'red'))
                errors += 1

        allplugins = self.plugins + self.prependers + self.appenders

        perrors = 0
        for plugin in allplugins:
            print()
            print("Linting Plugin ", fc.strcolor(str(plugin), 'cyan'),
                  'Config section:', fc.strcolor(str(plugin.section), 'cyan'))
            try:
                result = plugin.lint()
            except Exception as e:
                CrashStore.store_exception()
                print("ERROR: %s" % e)
                result = False

            if result:
                print(fc.strcolor("OK", "green"))
            else:
                perrors += 1
                errors += 1
                print(fc.strcolor("ERROR", "red"))
        print("%s plugins reported errors." % perrors)

        if "milter" in self.config.get('main', 'incomingport') \
                and self.config.get('performance', 'backend') != 'process':

            try:
                minfreethreads = self.config.getint('performance', 'minfreethreads')
                if minfreethreads < 1:
                    print(fc.strcolor('\nMilter enabled with "thread" backend but "minfreethreads < 1"', 'yellow'))
                    print("To keep milter responsive it is recommended to set minfreethreads >= 1\n"
                          "to make fuglu more resonsive.\n")
            except (configparser.NoSectionError, configparser.NoOptionError):
                print(fc.strcolor('\nMilter enabled with "thread" backend but "minfreethreads is not defined!"', 'yellow'))
                print("To keep fuglu-milter responsive it is recommended to set minfreethreads >= 1\n")

        if self.config.getboolean('main', 'versioncheck'):
            check_version_status(lint=True)

        return errors
Exemple #10
0
def check_version_status(lint=False):
    import pkg_resources
    """Check our version string in DNS for known issues and warn about them

    the lookup should be <7 chars of commitid>.<patch>.<minor>.<major>.versioncheck.fuglu.org
    in case of a release version, use 'release' instead of commit id

    lookup examples:
    - 0.9.1 -> 0.release.1.9.0.versioncheck.fuglu.org
    - 0.9.1rc5 -> 5.rc.1.9.0.versioncheck.fuglu.org
    - 0.9.1.dev20181204183236 -> 20181204183236.dev.1.9.0.versioncheck.fuglu.org

    DNS will return NXDOMAIN or 127.0.0.<bitmask>
    2: generic non security related issue
    4: low risk security issue
    8: high risk security issue
    """
    bitmaskmap = {
        2: "there is a known (not security related) issue with this version - consider upgrading",
        4: "there is a known low-risk security issue with this version - an upgrade is recommended",
        8: "there is a known high-risk security issue with this version - upgrade as soon as possible!",
    }

    try:
        version_obj = pkg_resources.parse_version(FUGLU_VERSION)._version
        if not isinstance(version_obj, pkg_resources.extern.packaging.version._Version):
            version_obj = None
    except AttributeError:
        # Older versions of setuptools (< 20.2.2) return a tuple.
        # Accessing "_version" will raise an attribute error.
        version_obj = None
    except Exception as e:
        # Skip test for other problems...
        logging.getLogger("fuglu.check_version_status").warning(str(e))
        version_obj = None

    if version_obj is None:
        logging.getLogger("fuglu.check_version_status")\
            .warning("Version string %s could not be parsed to pkg_resources version object", FUGLU_VERSION)
        return

    (major, minor, micro) = version_obj.release
    (stage_str, stage_id) = ('release', 0)

    if version_obj.dev:
        stage_str = version_obj.dev[0]
        stage_id = version_obj.dev[1]
    
    if version_obj.pre:
        stage_str = version_obj.pre[0]
        stage_id = version_obj.pre[1]

    if version_obj.post:
        stage_str = version_obj.post[0]
        stage_id = version_obj.post[1]

    parts = {
        'major': major,
        'minor': minor,
        'micro': micro,
        'stage_str': stage_str,
        'stage_id': stage_id
    }

    lookup = "{stage_id}.{stage_str}.{micro}.{minor}.{major}.versioncheck.fuglu.org".format(**parts)
    result = None
    try:
        result = socket.gethostbyname(lookup)
    except Exception:
        # DNS fails happen - try again next time
        pass

    if result is None:
        return

    ret = re.match(r'^127\.0\.0\.(?P<replycode>\d{1,4})$', result)
    if ret is not None:
        code = int(ret.groupdict()['replycode'])
        for bitmask, message in bitmaskmap.items():
            if code & bitmask == bitmask:
                logging.warn(message)
                if lint:
                    fc = FunkyConsole()
                    print(fc.strcolor(message, "yellow"))