Feature #7241
Group membership should be a type of its own.
| Status: | Accepted | Start date: | 04/26/2011 | |
|---|---|---|---|---|
| Priority: | Normal | Due date: | ||
| Assignee: | - | % Done: | 0% | |
| Category: | - | |||
| Target version: | 3.x | |||
| Affected Puppet version: | Branch: | |||
| Keywords: | ||||
Description
It’s very difficult right now to express declarative statements like:
- Ensure this user is not in this group, leave it alone otherwise
- Ensure this user is in this group without defining the user, leave it alone otherwise.
I propose that we move group membership to a type of its own. That would also allow us to abstract away the differences between different platforms, some of which consider membership to be an attribute of the group, some of which consider it to be an attribute of the user.
It would allow us to remove all the “authoritative” settings for user/group membership, as they would move to this type instead.
History
#1
Updated by Stefan Schulte about 2 years ago
So something like this?
groupmembership { 'testuser@testgroup':
ensure => present,
}
so I can just define one relationship at a time? Otherwise it would be pretty hard to define a title for the resource or decide what puppet resource groupmembership should return.
Then I have another question: How can I ensure that a user is JUST in group A,B,C? So purging not explicitly defined groupmemberships for just a sinlge user (membership => inclusive of the current user type)?
#2
Updated by Cody Herriges about 2 years ago
This would likely require the use of the parsedfile provider. I believe OSX is the only thing that can add users to a group outside the context of the user. I think the proposal is similar to the manages_members feature of the already present group type.
#3
Updated by Nigel Kersten about 2 years ago
Stefan Schulte wrote:
So something like this? […] so I can just define one relationship at a time? Otherwise it would be pretty hard to define a title for the resource or decide what
puppet resource groupmembershipshould return.
I don’t think we need to be hung up on namevars at all. Sure, we could use something multi-var like that, but I also really like:
groupmembership { 'ensure_testuser_in_testgroup':
ensure => present,
member => "testuser",
group => "testgroup",
}
Then I have another question: How can I ensure that a user is JUST in group A,B,C? So purging not explicitly defined groupmemberships for just a sinlge user (membership => inclusive of the current user type)?
That’s a good point. Maybe that’s the sort of operation that is best modelled at the user level?
#4
Updated by Stefan Schulte about 2 years ago
Nigel Kersten wrote:
I don’t think we need to be hung up on namevars at all. Sure, we could use something multi-var like that, but I also really like:
groupmembership { 'ensure_testuser_in_testgroup':
ensure => present,
member => "testuser",
group => "testgroup",
}
ok, but if testuser is not in testgroup, what exactly is out of sync here? Is member out of sync (it is from the group’s point of view) or is group out of sync (it is from the user’s point of view)?
I just fear that the number of resources I have to specify will explode when I want to specify a user which is in a lot of groups. I also prefer one message User[foo]/groups changed group1,group2,group3 to group4,group5,group6) over six messages about absent/present groupmembership resources but that is of course just my opinion.
You may also have problems when another resource requires your user because this resource cannot be sure that the user is completly set (or you have to manually require all the groupmembership resources as well)
As a sidenode: How do you ensure that you dont specify two conflicting memberships (if you prefer arbitrary titles)?
#5
Updated by John Bollinger almost 2 years ago
Nigel Kersten wrote:
It’s very difficult right now to express declarative statements like:
- Ensure this user is not in this group, leave it alone otherwise
- Ensure this user is in this group without defining the user, leave it alone otherwise.
I propose that we move group membership to a type of its own. That would also allow us to abstract away the differences between different platforms, some of which consider membership to be an attribute of the group, some of which consider it to be an attribute of the user.
It would allow us to remove all the “authoritative” settings for user/group membership, as they would move to this type instead.
I’m not very comfortable with this. I do agree that it would be useful to abstract away operating system differences, but I don’t see why it is necessary to introduce a new resource type to do so. Why can the User and / or Group providers not be made smarter instead? Just choose one of those resource types as the Puppet-level locus for managing group membership. This is more or less the standard Puppet approach: system differences are accommodated at the provider level.
As for ensuring group non-membership, that can be accomplished by adding a parameter to (say) User. For example:
user { "untrusted":
excluded_groups => [ 'extra_privileged' ]
}
Providers must of course do the right thing with that, but that’s no harder than the problem that a Groupmembership provider would face.
And as for managing group membership without managing the users involved:
I’m not very keen on the concept in the first place. Moreover, such a feature would be meaningless if one were able to manage a User’s groups as its only managed attributes (as in the above example).
It could nevertheless be accomplished by making Group the Puppet locus for managing group membership on all systems (but see below).
For systems with the concept of a primary group for each user (i.e. most of those on which Puppet currently runs), membership in a primary group is not comparable to membership in a secondary group. On such systems, group membership cannot be managed independently of users in a consistent manner. For example, what happens if one attempts to ensure a user not a member of his primary group?
Overall, I think the underlying issue is an old one: whether group membership is an attribute of users or of groups. In an abstract sense it is both, which is the root of the problem. Elevating group membership to its own type does not solve that problem; instead, it complicates it further.
#6
Updated by Daniel Pittman almost 2 years ago
Nigel Kersten wrote:
I propose that we move group membership to a type of its own. That would also allow us to abstract away the differences between different platforms, some of which consider membership to be an attribute of the group, some of which consider it to be an attribute of the user.
I’m not very comfortable with this. I do agree that it would be useful to abstract away operating system differences, but I don’t see why it is necessary to introduce a new resource type to do so. Why can the User and / or Group providers not be made smarter instead? Just choose one of those resource types as the Puppet-level locus for managing group membership. This is more or less the standard Puppet approach: system differences are accommodated at the provider level.
As for ensuring group non-membership, that can be accomplished by adding a parameter to (say) User. For example:
user { "untrusted": excluded_groups => [ 'extra_privileged' ] }
So, the problem with this approach is that you can’t reasonably express “only this membership set” in a useful way, unless we introduce something even “smarter” like glob / regex matching in that parameter. Otherwise, consider the case where someone creates a new, local group, “frobnitz”, and adds “untrusted” to it. If “frobnitz” gates access to something, it is probably … not desirable that “untrusted” get it. :)
This is suggesting something analogous to building firewall rules: if you try to express what shouldn’t be, you will end up with an infinitely long list, or end up missing something. Better to whitelist what is permitted, in the cases where that is the applicable model. (eg: some, but not all, security models for group membership of users. :)
#7
Updated by Forrest Aldrich over 1 year ago
For my own purposes, being able to specify a string of users that are members in a group would be useful (keeping it simple). For example:
@group { "puppet":
ensure => present,
gid => '5555',
members => 'user,user2,user3,blah',
}
Where the members do not necessarily need to be virtual users. I can see how this might create some conflicts in how the file is managed by other mechanisms. Keeping it simple would be key :–)
#8
Updated by Jeremy Huntwork over 1 year ago
The stable documentation already says that the parameter ‘members’ exists where the feature ‘manages_members’ is available. I don’t know if this was considered yet, but for shadow-based systems, ensuring explicit membership for a group is as easy as:
gpasswd -M user1,user2,... group
You could, therefore, enable the manages_members feature for shadow-based systems and expect that the ‘members’ param for the group type is a complete list of its secondary members, if specified. One issue I can see with doing that is it could conflict with what may have been declared for a user with the groups param. It does, however, fit better with the declarative nature of puppet – group, be this way, have these secondary members.
There is another workaround that I can see: change the groups param of the user type to be an exhaustive, complete list of secondary groups that this user belongs to, instead of a list of groups that should be added to the user’s set of current groups, which appears to be the default behavior. If it’s felt that appending should be allowed, then add a param which can switch the list meanings to ‘append’ vs ‘complete’. If complete is chosen and groups == undef, then remove the user from any secondary groups, the same as doing:
usermod -G '' user
As an aside, I was able to work around the current limitations by doing:
define users::groupmng ($members) {
$group = $title
# Ensure group has only members specified
exec { "gpasswd -M ${members} ${group}":
path => ['/bin','/usr/bin','/sbin','/usr/sbin'],
require => [Group["$group"], User["${members}"]],
}
}
class users::human {
$wheel = ['...']
...
}
users::groupmng { 'wheel':
members => $users::human::wheel
}
#9
Updated by Stefan Schulte over 1 year ago
Jeremy Huntwork wrote:
There is another workaround that I can see: change the groups param of the user type to be an exhaustive, complete list of secondary groups that this user belongs to, instead of a list of groups that should be added to the user’s set of current groups, which appears to be the default behavior.
There already is ;–) Try the membership parameter
Just append missing groups:
user { 'testuser':
groups => [ 'wheel', 'video' ]
membership => minimum # default
}
Also remove groups that are not mentioned:
user { 'testuser':
groups => [ 'wheel', 'video' ]
membership => inclusive # specify the complete list
}
#10
Updated by Jeremy Huntwork over 1 year ago
Stefan Schulte wrote:
There already is ;–) Try the [
membershipparameter][1]
Ah nice, I missed that one. Thanks for the tip.