Hi,

attached please find a first cut of an HBAC tester command to CLI,
FreeIPA ticket https://fedorahosted.org/freeipa/ticket/386

The idea behind this plugin is to re-use pyhbac module provided by SSSD
project which is Python bindings for SSSD's libipa_hbac code used for
actual HBAC rule execution. This requires libipa_hbac-python package.

There are four modes implemented by the plugin given (user, source host,
target host, service), attempt to login user coming from source host to
target host's service:

1. Use all enabled HBAC rules in IPA database to simulate
[root@host0 ~]# ipa  hbactest --user=a1a --srchost=foo --host=bar
--service=ssh
--------------------
Access granted: True
--------------------

2. Use all enabled HBAC rules in IPA database + explicitly specified
(disabled) rules
[root@host0 ~]# ipa  hbactest --user=a1a --srchost=foo --host=bar
--service=ssh --rules=my-second-rule
--------------------
Access granted: True
--------------------

3. Use only explicitly specified HBAC rules
[root@host0 ~]# ipa  hbactest --user=a1a --srchost=foo --host=bar
--service=ssh --rules=my-second-rule,new-rule --validate
--------------------
Access granted: True
--------------------
  Passed rules: new-rule
  Denied rules: my-second-rule

4. Get detailed result of simulation for all enabled HBAC rules:
[root@host0 ~]# ipa  hbactest --user=a1a --srchost=foo --host=bar
--service=ssh  --validate
--------------------
Access granted: True
--------------------
  Passed rules: allow_all
  Denied rules: my-second-rule, my-third-rule, myrule

--validate option forces to run detailed simulation and report per-rule
results. Results are: passed, denied, error. The latter one is for
wrongly specified rules which should not be enabled.

When --validate specified together with --rules, only HBAC rules
specified on the command line are considered.

I'm still not sure if running simulation against all disabled HBAC rules
in databse is worth it.

-- 
/ Alexander Bokovoy
# Authors:
#   Alexander Bokovoy <aboko...@redhat.com>
#
# Copyright (C) 2011  Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
Simulate use of Host-based access controls

HBAC rules control who can access what services on what hosts and from where. 
You can use HBAC to control which users or groups on a source host can
access a service, or group of services, on a target host.

Since applying HBAC rules implies use of a production environment,
this plugin aims to provide simulation of HBAC rules evaluation without
having access to the production environment.

EXAMPLES:

 Test user coming from source host to a service on a named host against 
existing 
 enabled rules. If --rules specified (whether enabled or disabled, does not 
matter).
 simulate enabling of the specified rules in addition to already enabled ones 
and test 
 the login of the user. If --validate is specified, provide detailed per-rule 
result 
 of simulation. If both --rules and --validate are specified, apply simulation 
to --rules 
 only.

   ipa hbactest --user= --srchost= --host= --service= [--rules=rules-list] 
[--validate]
"""

from ipalib import api, errors, output
from ipalib import Command, List, Str, Flag
from ipalib.cli import to_cli
from ipalib import _, ngettext
import pyhbac

class hbactest(Command):

    has_output_params = (
        List('passed',
            label=_('Passed rules'),
        ),
        List('denied',
            label=_('Denied rules'),
        ),
        List('error',
            label=_('Incorrect rules'),
        ),
    )

    has_output = output.standard_entry

    takes_options = (
        Str('user',
            cli_name='user',
            label=_('User name'),
            primary_key=True,
            required=True,
        ),
        Str('sourcehost',
            cli_name='srchost',
            label=_('Source host'),
            required=True,
        ),
        Str('targethost',
            cli_name='host',
            label=_('Target host'),
            required=True,
        ),
        Str('service',
            cli_name='service',
            label=_('Service'),
            required=True,
        ),
        List('testrules?',
             cli_name='rules',
             label=_('Rules to test'),
        ),
        Flag('validate?',
             cli_name='validate',
             label=_('Validate rules'),
        ),
    )

    def __convert_to_ipa_rule(self, rule):
        # convert a dict with a rule to an pyhbac rule
        ipa_rule = pyhbac.HbacRule(rule['cn'][0])
        ipa_rule.enabled = rule['ipaenabledflag'][0]
        # Following code attempts to process rule systematically
        structure = (('user',       'memberuser',    'user',    'group',        
ipa_rule.users),
                     ('host',       'memberhost',    'host',    'hostgroup',    
ipa_rule.targethosts),
                     ('sourcehost', 'sourcehost',    'host',    'hostgroup',    
ipa_rule.srchosts),
                     ('service',    'memberservice', 'hbacsvc', 'hbacsvcgroup', 
ipa_rule.services),
                    )
        for element in structure:
            category = '%scategory' % (element[0])
            if category in rule and rule[category][0] == u'all':
                # rule applies to all elements
                element[4].category = set([pyhbac.HBAC_CATEGORY_ALL])
            else:
                # rule is about specific entities
                # Check if there are explicitly listed entities
                attr_name = '%s_%s' % (element[1], element[2])
                if attr_name in rule:
                    element[4].names = rule[attr_name]
                # Now add groups of entities if they are there
                attr_name = '%s_%s' % (element[1], element[3])
                if attr_name in rule:
                    element[4].groups = rule[attr_name]
        return ipa_rule

    def execute(self, *args, **options):
        # First receive all needed information:
        # 1. HBAC rules (whether enabled or disabled)
        # 2. Required options are (user, source host, target host, service)
        # 3. Options: rules to test, validation request
        rules = []
        hbacset = self.api.Command.hbacrule_find()['result']
        # We have some rules, import them
        for rule in hbacset:
            ipa_rule = self.__convert_to_ipa_rule(rule)
            if options['validate'] and 'testrules' in options:
                if ipa_rule.name in options['testrules']:
                    ipa_rule.enabled = True
                    rules.append(ipa_rule)
            else:
                if 'testrules' in options and ipa_rule.name in 
options['testrules']:
                    # Explicitly add any rule that was specified in testrules
                    ipa_rule.enabled = True
                if ipa_rule.enabled:
                    rules.append(ipa_rule)
        # Rules are converted to pyhbac format, we can test them
        request = pyhbac.HbacRequest()
        request.user.name = options['user']
        request.service.name = options['service']
        request.srchost.name = options['sourcehost']
        request.targethost.name = options['targethost']
        passed_rules = []
        denied_rules = []
        error_rules = []
        if options['validate']:
            # Validate runs rules one-by-one and reports failed ones
            for ipa_rule in rules:
                res = request.evaluate([ipa_rule])
                self.log.info("Evaluated rule %s, result: %d" % (ipa_rule.name, 
res))
                if res == pyhbac.HBAC_EVAL_ALLOW:
                    passed_rules.append(ipa_rule.name)
                if res == pyhbac.HBAC_EVAL_DENY:
                    denied_rules.append(ipa_rule.name)
                if res == pyhbac.HBAC_EVAL_ERROR:
                    error_rules.append(ipa_rule.name)
            access_granted = len(passed_rules) > 0
        else:
            res = request.evaluate(rules)
            access_granted = (res == pyhbac.HBAC_EVAL_ALLOW)
        return dict(summary=_(u'Access granted: %s') % (access_granted), 
                    result=dict(passed=passed_rules, denied=denied_rules, 
errors=error_rules),
                    value=unicode(access_granted))

api.register(hbactest)

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to