Conary:User Group Spec
From rPath Wiki
| Conary Developer Community | Go to the community main page |
|
User/Group Specification
Introduction
Conary handles users and groups very differently from RPM. In RPM, %pre scripts can create users and groups, but Conary does not have those by design. Instead, Conary has special handling for components which provide user information, one user per component.
Any file belonging to a user other than root or a group other than root automatically has a dependency on a component which provides that user or group. These special components which create users have names that start with info- and are always installed first, before any packages which require them, and are installed in a separate transaction so that the users and groups have been created before the packages that use those users and groups are installed on the system.
This design document describes both the semantics and mechanism that we have implemented.
Schema and Semantics
First, let's define the information that a packager can set, without regard to where it is put, or how it is acted on.
General
Package-specified users/groups should not have passwords or shadow information like expiry. Also, in every case where a user or group name or numeric id is possible, we always list the name, and always list a preferred number that goes with that name in order to keep systems as consistent as possible.
We should never remove users or groups. To do so would be a security risk. If a user or group is removed from the system database, but a file (or other resource) is left owned by that user/group, then when another different user or group is added and re-uses the number, that new user can access the file (or other resource). The consequences of that access capability cannot be known in general.
Note, however, that removing a supplemental group from a user is an explicit reduction in privilege, and therefore supplemental groups should ideally be removed from a user on the system if they are removed from all troves that are installed on the system that specify that supplemental group for that user. This has not been implemented, and it is not clear that it will be implemented.
Users
We do not create home directories for package-specified users; those directories should be packaged instead. Also, since we always provide a suggested id, the -r option doesn't make sense (except internally as a fallback if the id is not available). So thinking in terms of useradd options, we want to allow people to specify:
-u | preferred user id |
-g | group (but only by name, defaults to same as user) |
-G | required supplemental group list (but only additions) |
-c | comment |
-d | home dir (default to /)
|
-s | shell (default to /sbin/nologin)
|
So the changes relative to the command-line option semantics are: for -g, we only allow a name; for -G, we only list additions to the group list required for the package, for -d, we default to /, and for -s, we default to /sbin/nologin.
The -G option is special. The -u, -g, -c, -d, and -s options are used only when creating the user. The -G option, if specified, provides groups that must be added to the specified user.
Groups
For adding groups, there are not so many options:
-g | preferred group id |
Mechanism
Troves
Every file that has an owner or group other than root is automatically given a userinfo or groupinfo dependency (this dependency has been disabled temporarily for migration purposes, but will be enabled shortly). These dependencies are satisfied by info-owner:user and info-group:group components. For user's primary groups, the info-owner:user components will provide both the userinfo and groupinfo dependencies. Each :user or :group component can create only a single user (including primary group) or group.
A :user component is created by a UserInfoRecipe and a :group component by a GroupInfoRecipe. Internally, these two kinds of recipes create files that are tagged as user-info and group-info files. If there is no tag handler installed, then conary will modify /etc/passwd and/or /etc/group itself. When the tag handler is installed, it will be used in preferrence to Conary's internal handling, which is intended only for bootstrapping.
Recipes
Defining Users and Groups
In a UserInfoRecipe, you can call exactly one instance of one method:
r.User('name', preferred_uid, group='maingroupname',
groupid=preferred_gid, homedir='/home/dir',
comment='comment', shell='/path/to/shell',
supplemental=['list', 'of', 'groups'])
where the optional fields have reasonable default values:
group | name |
groupid | preferred_uid
|
homedir | / |
comment | empty |
shell | '/sbin/nologin'
|
supplemental | empty |
We prefer to use version number 1, and the name of the recipe must be info-name where name is the first argument to the User method. Note that this UserInfoRecipe will provide the user's primary group, which normally has the same name as the user. It will require any group listed in the supplemental list.
In a GroupInfoRecipe, you can have exactly a single instance of one of two methods:
To add a supplemental group to a user for supplemental groups that are not associated with a user, you can use:
r.SupplementalGroup('user', 'group', preferred_gid)
This is useful mainly for things like adding supplemental groups to the apache user so that the web server can see data. It won't get very much use, so the fact that it is slightly ugly is not a big deal.
If you need to create only a group, you specify only:
r.Group('name', preferred_gid)
Like r.SupplementalGroup, this should be quite rare. You do not need to use r.Group() for groups that are the primary group for a user; you need it only for groups that exist independently.
Utilizing Users and Groups
When you use Ownership to set the owner/group of a file, the proper dependencies are automatically created. (Well, they will be, once we finish buildling the info-* components.)
Sometimes, including daemons being run without priviledges, the point of having a separate user/group for the daemon is that it gives no priviledges for filesystem I/O. In these cases, it is important that no file on the system be owned by that user/group.
For those cases, you need to explicitly tell Conary that the file (generally an executable) utilizes the user/group even though it is not owned by that user/group. For this case, we will have r.UtilizeUser() and r.UtilizeGroup():
r.UtilizeUser('username', <filespec(s)>)
and
r.UtilizeGroup('groupname', <filespec(s)>)
Example
Simple example that creates the haldaemon user with a group haldaemon and also adds the haldaemon user to the disk group:
class UserHaldaemon(UserInfoRecipe):
name = "info-haldaemon"
version = "1"
def setup(r):
r.User('haldaemon', 68, comment='HAL daemon',
supplemental=['disk'])
