summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/server/configuration.txt2
-rw-r--r--src/lib/Bcfg2/Server/Lint/Metadata.py38
-rw-r--r--src/lib/Bcfg2/Server/Plugins/GroupLogic.py20
-rwxr-xr-xtools/posixusers_baseline.py10
4 files changed, 59 insertions, 11 deletions
diff --git a/doc/server/configuration.txt b/doc/server/configuration.txt
index f93b172ef..d3fa42601 100644
--- a/doc/server/configuration.txt
+++ b/doc/server/configuration.txt
@@ -147,7 +147,7 @@ Consequently, you simply have to run:
.. code-block:: bash
chown bcfg2:bcfg2 /var/run/bcfg2-server
- chmod 0644 /var/run/bcfg2-server
+ chmod 0755 /var/run/bcfg2-server
Additionally, the server daemon itself supports dropping privileges
natively in 1.3. Simply add the following lines to ``bcfg2.conf``::
diff --git a/src/lib/Bcfg2/Server/Lint/Metadata.py b/src/lib/Bcfg2/Server/Lint/Metadata.py
index bf19c25fc..248b1610c 100644
--- a/src/lib/Bcfg2/Server/Lint/Metadata.py
+++ b/src/lib/Bcfg2/Server/Lint/Metadata.py
@@ -106,15 +106,35 @@ class Metadata(ServerPlugin):
"client")
def duplicate_groups(self):
- """ Check for groups that are defined more than once. We
- count a group tag as a definition if it a) has profile or
- public set; or b) has any children."""
- allgroups = [
- g
- for g in self.metadata.groups_xml.xdata.xpath("//Groups/Group") +
- self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group")
- if g.get("profile") or g.get("public") or g.getchildren()]
- self.duplicate_entries(allgroups, "group")
+ """ Check for groups that are defined more than once. There
+ are two ways this can happen:
+
+ 1. The group is listed twice with contradictory options.
+ 2. The group is listed with no options *first*, and then with
+ options later.
+
+ In this context, 'first' refers to the order in which groups
+ are parsed; see the loop condition below and
+ _handle_groups_xml_event above for details. """
+ groups = dict()
+ duplicates = dict()
+ for grp in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \
+ self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"):
+ grpname = grp.get("name")
+ if grpname in duplicates:
+ duplicates[grpname].append(grp)
+ elif len(grp.attrib) > 1: # group has options
+ if grpname in groups:
+ duplicates[grpname] = [grp, groups[grpname]]
+ else:
+ groups[grpname] = grp
+ else: # group has no options
+ groups[grpname] = grp
+ for grpname, grps in duplicates.items():
+ self.LintError("duplicate-group",
+ "Group %s is defined multiple times:\n%s" %
+ (grpname,
+ "\n".join(self.RenderXML(g) for g in grps)))
def duplicate_entries(self, allentries, etype):
""" Generic duplicate entry finder.
diff --git a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
index 1da7c8fec..ebcab1a6b 100644
--- a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
+++ b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
@@ -3,6 +3,7 @@ template to dynamically set additional groups for clients. """
import os
import lxml.etree
+from threading import local
import Bcfg2.Server.Plugin
from Bcfg2.Server.Plugins.Metadata import MetadataGroup
@@ -30,14 +31,32 @@ class GroupLogic(Bcfg2.Server.Plugin.Plugin,
""" GroupLogic is a connector plugin that lets you use an XML
Genshi template to dynamically set additional groups for
clients. """
+ # perform grouplogic later than other Connector plugins, so it can
+ # use groups set by them
+ sort_order = 1000
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Connector.__init__(self)
self.config = GroupLogicConfig(os.path.join(self.data, "groups.xml"),
should_monitor=True)
+ self._local = local()
+ # building is a thread-local set that tracks which machines
+ # GroupLogic is getting additional groups for. If a
+ # get_additional_groups() is called twice for a machine before
+ # the first call has completed, the second call returns an
+ # empty list. This is for infinite recursion protection;
+ # without this check, it'd be impossible to use things like
+ # metadata.query.in_group() in GroupLogic, since that requires
+ # building all metadata, which requires running
+ # GroupLogic.get_additional_groups() for all hosts, which
+ # requires building all metadata...
+ self._local.building = set()
def get_additional_groups(self, metadata):
+ if metadata.hostname in self._local.building:
+ return []
+ self._local.building.add(metadata.hostname)
rv = []
for el in self.config.XMLMatch(metadata).findall("Group"):
if el.get("category"):
@@ -45,4 +64,5 @@ class GroupLogic(Bcfg2.Server.Plugin.Plugin,
category=el.get("category")))
else:
rv.append(el.get("name"))
+ self._local.building.discard(metadata.hostname)
return rv
diff --git a/tools/posixusers_baseline.py b/tools/posixusers_baseline.py
index 1f89c7cb6..06925beed 100755
--- a/tools/posixusers_baseline.py
+++ b/tools/posixusers_baseline.py
@@ -2,6 +2,7 @@
import grp
import sys
+import logging
import lxml.etree
import Bcfg2.Logger
import Bcfg2.Options
@@ -22,6 +23,7 @@ class CLI(object):
components=[self, POSIXUsers]).parse()
config = lxml.etree.Element("Configuration")
self.users = POSIXUsers(config)
+ self.logger = logging.getLogger('posixusers_baseline.py')
def run(self):
baseline = lxml.etree.Element("Bundle", name="posixusers_baseline")
@@ -34,7 +36,13 @@ class CLI(object):
continue
entry.set(attr, str(data[idx]))
if entry.tag == 'POSIXUser':
- entry.set("group", grp.getgrgid(data[3])[0])
+ try:
+ entry.set("group", grp.getgrgid(data[3])[0])
+ except KeyError:
+ self.logger.warning(
+ "User %s is a member of nonexistent group %s" %
+ (entry.get("name"), data[3]))
+ entry.set("group", str(data[3]))
for group in self.users.user_supplementary_groups(entry):
lxml.etree.SubElement(entry, "MemberOf", group=group[0])