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

Reply via email to