On 22.07.2011 23:10, Alexander Bokovoy wrote: >> So this is a little confusing. I thought --rules limited the rules that >> were considered. Maybe I'm misunderstanding it. > --validate + --rules gives limitation, --rules alone adds more rules to > the existing test set which is all enabled rules in IPA. I reworked a bit command line interface to avoid confusion like that.
# ipa hbactest --help Usage: ipa [global-options] hbactest [options] Options: -h, --help show this help message and exit --user=STR User name --srchost=STR Source host --host=STR Target host --service=STR Service --rules=LIST Rules to test. If not specified, all enabled rules are tested --detail Detail rule execution --all Include all enabled IPA rules into test Now if you specify --rules, hbactest will only try to simulate login using these rules. You would need to add --all to force considering all IPA enabled rules. When no --rules are specified, simulation is run against all enabled IPA rules. --validate got replaced by --detail which simply tries to run simulation one by one and report results for each rule. You can apply it for any run, with or without --rules and --all. If --rules contains a name of non-existent rule, it is simply ignored. So if I asked to verify against --rule=foobar where there is no such rule, Should there be error message for such cases? Right now you'll get False (access is not granted) and --detail will not show any rules. Now, the only mode left out is batch verification of all disabled rules for purpose of checking their correctness. Suppose we have a switch --show-invalid that takes all IPA rules and runs a simulation request against them, reporting the ones that are invalid only. Such a request could be done without any specific (user, source host, target host, service) tuple because we are only interested in HBAC_EVAL_ERROR return code which is independent of input parameters. Unfortunately all we can tell in this case is that rule is incorrect, without much details. Probably some improvement for libipa_hbac is needed, like converting request result into a bit field and returning detailed cause of error per tuple element. Current version is attached. It still lacks unit tests. -- / Alexander Bokovoy
From 889510430cb46006729df49384805589400c1b5c Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy <aboko...@redhat.com> Date: Fri, 22 Jul 2011 16:30:44 +0300 Subject: [PATCH] Add hbactest command. 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 --all -------------------- 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 --detail -------------------- 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 --detail -------------------- Access granted: True -------------------- Passed rules: allow_all Denied rules: my-second-rule, my-third-rule, myrule --detail 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 --all *is not* specified together with --rules, only HBAC rules specified on the command line are considered. Only rules existing in IPA database are tested. They may be in enabled or disabled disabled state. Specifying them through --rules option explicitly enables them only in simulation run. --- API.txt | 12 +++ VERSION | 2 +- freeipa.spec.in | 5 + ipalib/plugins/hbactest.py | 216 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 1 deletions(-) create mode 100644 ipalib/plugins/hbactest.py diff --git a/API.txt b/API.txt index 42a212b1ebda0f8e1f45b016c5ba421f16ffb24c..8fbea271e9d27d87999385aa45922034e378eae1 100644 --- a/API.txt +++ b/API.txt @@ -1321,6 +1321,18 @@ option: Str('version?', exclude='webui', flags=['no_option', 'no_output']) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user") +command: hbactest +args: 0,7,3 +option: Str('user', cli_name='user', label=Gettext('User name', domain='ipa', localedir=None), primary_key=True) +option: Str('sourcehost', cli_name='srchost', label=Gettext('Source host', domain='ipa', localedir=None)) +option: Str('targethost', cli_name='host', label=Gettext('Target host', domain='ipa', localedir=None)) +option: Str('service', cli_name='service', label=Gettext('Service', domain='ipa', localedir=None)) +option: List('testrules?', cli_name='rules', label=Gettext('Rules to test. If not specified, all enabled rules are tested.', domain='ipa', localedir=None), multivalue=True) +option: Flag('detail?', autofill=True, cli_name='detail', default=False, label=Gettext('Detail rule execution', domain='ipa', localedir=None)) +option: Flag('all?', autofill=True, cli_name='all', default=False, label=Gettext('Include all enabled IPA rules into test', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed') +output: Output('result', <type 'dict'>, Gettext('Details of simulation', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, Gettext('Result of simulation', domain='ipa', localedir=None)) command: host_add args: 1,14,3 arg: Str('fqdn', validate_host, attribute=True, cli_name='hostname', label=Gettext('Host name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, required=True) diff --git a/VERSION b/VERSION index 98e92c5dd3a6902e8552fa81f491fec6e1633c12..0abf5962f983e42cdcef50e65ecdbd06c9a85030 100644 --- a/VERSION +++ b/VERSION @@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=10 +IPA_API_VERSION_MINOR=11 diff --git a/freeipa.spec.in b/freeipa.spec.in index 35ff84576da61cdd83875ca318e04998f9bef6a7..5276d1d169a3d8399e940ad8e1805a43f0b01d28 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -59,6 +59,7 @@ BuildRequires: python-kerberos BuildRequires: python-rhsm BuildRequires: pyOpenSSL BuildRequires: pylint +BuildRequires: libipa_hbac-python %description IPA is an integrated solution to provide centrally managed Identity (machine, @@ -201,6 +202,7 @@ Requires: python-netaddr >= 0.7.5-3 %else Requires: python-netaddr %endif +Requires: libipa_hbac-python Obsoletes: ipa-python >= 1.0 @@ -511,6 +513,9 @@ fi %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf %changelog +* Fri Jul 22 2011 Alexander Bokovoy <aboko...@redhat.com> - 2.0.90-7 +- Add libipa_hbac-python dependency for hbactest plugin + * Thu Jul 14 2011 Rob Crittenden <rcrit...@redhat.com> - 2.0.90-6 - Add ipa-csreplica-manage tool. diff --git a/ipalib/plugins/hbactest.py b/ipalib/plugins/hbactest.py new file mode 100644 index 0000000000000000000000000000000000000000..509edd72c07d7b20a99ed1dd84d2dc6e0993e388 --- /dev/null +++ b/ipalib/plugins/hbactest.py @@ -0,0 +1,216 @@ +# 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 is specified simulate enabling of the specified rules and test the login + of the user using only these rules. + + If --detail is specified, provide detailed per-rule result of simulation. + + If both --rules and --all are specified, apply simulation to --rules _and_ + all IPA enabled rules. + + If no --rules specified, simulation is run against all IPA enabled rules. + + ipa hbactest --user= --srchost= --host= --service= + [--rules=rules-list] [--detail] [--all] +""" + +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 + +def convert_to_ipa_rule(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 + + +class hbactest(Command): + + has_output = ( + output.summary, + output.Output('result', dict, _('Details of simulation')), + output.Output('value', unicode, _('Result of simulation'), ['no_display']), + ) + + takes_options = ( + Str('user', + cli_name='user', + label=_('User name'), + primary_key=True, + ), + Str('sourcehost', + cli_name='srchost', + label=_('Source host'), + ), + Str('targethost', + cli_name='host', + label=_('Target host'), + ), + Str('service', + cli_name='service', + label=_('Service'), + ), + List('testrules?', + cli_name='rules', + label=_('Rules to test. If not specified, all enabled rules are tested.'), + ), + Flag('detail?', + cli_name='detail', + label=_('Detail rule execution'), + ), + Flag('all?', + cli_name='all', + label=_('Include all enabled IPA rules into test'), + ), + ) + + 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 = convert_to_ipa_rule(rule) + if 'testrules' in options: + if ipa_rule.name in options['testrules']: + ipa_rule.enabled = True + rules.append(ipa_rule) + elif options['all'] and ipa_rule.enabled: + # Option --all forces to include all enabled IPA rules into test + rules.append(ipa_rule) + else: + 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['detail']: + # 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) + + result = {} + + result['summary'] = _(u'Access granted: %s') % (access_granted) + + result['result'] = {} + if len(passed_rules) > 0: + result['result']['passed'] = passed_rules + if len(denied_rules) > 0: + result['result']['denied'] = denied_rules + if len(error_rules) > 0: + result['result']['error'] = error_rules + + result['value'] = unicode(access_granted) + return result + + def output_for_cli(self, textui, output, *args, **options): + """ + Command.output_for_cli() uses --all option to decide whether to print detailed output. + We use --detail to allow that, thus we need to redefine output_for_cli(). + """ + labels = dict((p.name, unicode(p.label)) for p in self.output_params()) #pylint: disable=E1102 + flags = dict((p.name, p.flags) for p in self.output_params()) #pylint: disable=E1102 + + if options.get('detail', False): + print_all = True + else: + print_all = False + + order = [p.name for p in self.output_params()] #pylint: disable=E1102 + for o in self.output: + outp = self.output[o] + if 'no_display' in outp.flags: + continue + result = output[o] + if isinstance(result, dict): + textui.print_entry(result, order, labels, flags, print_all) + elif isinstance(result, unicode): + if o == 'summary': + textui.print_summary(result) + else: + textui.print_indented(result) + return 0 + +api.register(hbactest) + + -- 1.7.6
_______________________________________________ Freeipa-devel mailing list Freeipa-devel@redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel