Sunday, June 6, 2010

Credentials and Projects for a Solaris 10 SMF-Managed Service

Introduction

Some posts ago we've learnt about Solaris 10 basic Projects and Resource Caps administration. In that article we examined Solaris 10 Projects facility and how it enables the administrator to group and organize running processes into tasks which are running in a project context. Since projects may define resource caps, processes running inside a project will be subject to such caps.

In the previous post we discovered how easy it is for an administrator to define Solaris 10 projects and assign processes to them. We also said that there was a little gotcha: how would an administrator define the credential (and/or the Solaris 10 project) under which an SMF-managed service will run? In this articles we'll explore the basic Solaris 10 SMF Service Manifest DTD to discover how to define user credentials, privileges and projects for a specific SMF service instance.

The SMF Service Manifest DTD

Solaris 10 SMF uses manifests to describe the characteristics of a managed service. The manifest DTD (Solaris 10, version 1) can be found on:

/usr/share/lib/xml/dtd/service_bundle.dtd.1

Although in some cases there's no need for an administrator to define a manifest for an SMF service (as in the case of inetd-manage services because Solaris 10 will build a manifest for you) it's not bad to quickly glance at the service DTD, at least to discover what can be done.

Method Context

The method context, defined by:

<!ELEMENT method_context
  ( (method_profile | method_credential)?,
    method_environment? ) >

<!ATTLIST method_context
  working_directory CDATA ":default"
  project           CDATA ":default"
  resource_pool     CDATA ":default" >

lets the administrator define credentials and resource managements attributes for an execution method. The method_context element defines the following three attributes:

NameDescription
working_directoryThe working directory to launch the execution method from. The :default token can be used to indicate the user specified for the method (by means of the method_profile or the method_credential element.)
projectThe project under which to run the current execution method. The project can be specified in either the numeric or the text form. The :default token can be used to indicate the use of the default project for the user specified (see working_directory description.)
resource_poolThe resource pool name to launch the method on. The :default token can be used to indicate the default pool for the specified project (see project description above.)

Method Credentials

The method credentials, as outlined in the previous section, can be specified with the optional method_credential subelement of the method_context element. The method_credential element is defined as follows:

<!ELEMENT method_credential EMPTY>

<!ATTLIST method_credential
  user             CDATA #REQUIRED
  group            CDATA ":default"
  supp_groups      CDATA ":default"
  privileges       CDATA ":default"
  limit_privileges CDATA ":default" >

The attributes of the method_credential element are defined as follows:

NameDescription
userThe user id for the current execution method. The user id can be specified in either the numeric or the text form.
groupThe group id for the current execution method. The group id can be specified in either the numeric or the text form. The :default token can de used to specify the default group for the specified user (see user attribute above.)
supp_groupsOptional supplemental groups to associate with the current execution method. A list of groups ids can be specified using a space as a separator. If absent or when the :default token is specified, initgroups(3C) will be specified.
privilegesAn optional privilege set.
limit_privilegesAn optional limit privilege set.

If you're wondering about privileges, please check out the official Solaris RBAC documentation.

An Example Manifest

Many Solaris 10 bundled SMF services use RBAC, hence their manifest make extensive use of such elements. You can, for example, check the default PostgreSQL service manifest:

$ svccfg export postgresql
[...snip...]
<instance name='version_81' enabled='false'>
      <method_context project=':default' resource_pool=':default' working_direct
ory=':default'>
        <method_credential group='postgres' limit_privileges=':default' privileg
es=':default' supp_groups=':default' user='postgres'/>
      </method_context>
[...snip...]

In the service manifest fragment above you can see how the postgresql service definition leverages RBAC and projects to define credential, privileges and resource pools and caps for the default PostgreSQL service.

Flexibility and Simplicity

This mechanisms is indeed simple and flexible: since Solaris 10 RBAC and Solaris 10 Projects use loosely coupled layers to define privileges and resource caps, it's easy to establish a relation between and user and a project (or a privilege set, or whatever), and then Solaris will make the rest for you. As you can see in the previous manifest fragment, there's most of the tokens are :default. The PostgreSQL processes will run under the specified user credentials (postgres) and all of the other parameters (resource pool, project, working directory, privileges, etc.) will be discovered at runtime by Solaris.

Should you need to cap PostgreSQL resources, just define a suitable project and establish it as the default project for the postgres user: you won't (almost) ever need to change the service manifest.

Inetd-Managed Services

As explained in another post, configuring an inetd-managed service on Solaris 10 is almost a one-liner. There's was a gotcha, though. Although the user specified with the legacy inetd syntax was assigned to a default project with a resource cap, I noticed that Solaris 10 wasn't honoring it. Let's try and check the manifest that Solaris 10 generated for us:

$ svccfg export svc:/network/svn/tcp
[...snip...]
<exec_method name='inetd_start' type='method' exec='/opt/csw/bin/svnserve -i -r /home/svnuser/svnrepos' timeout_seconds='0'>
  <method_context>
    <method_credential user='svnuser' group='svngroup'/>
  </method_context>
</exec_method>
[...snip...]

There's no default project specified indeed! Let's notice that a missing project attribute does not tell Solaris 10 to use the default project for the specified user. To solve this, just dump the service definition to a file, modify it and reimport it:

$ svccfg export svc:/network/svn/tcp > svn.xml
$ [add project=':default' attribute]
# svccfg -v import svn.xml

Just check that the service manifest has been correctly import and restart the service.

Conclusion

Solaris 10 offers great and sophisticated tools to ease the life of the system administrator and enable the deployment of flexible and controlled execution environments. But beware: sophisticated doest not imply complicated in the Solaris 10 universe. On the contrary: as much as I discover and use Solaris 10 as much I admire and appreciate its clean and usable administration tools. As a minimum, I never have to modify a descriptor by hand. There always is a command line interface tool to protect me from errors and to ease my scripting experience. Just look for the corresponding *adm or *cfg command to do your job. Solaris 10 documentation and the community around OpenSolaris, moreover, are great places where to go and find the help you need.

For an alternate example that emphasizes the flexibility of the SMF service manifests, have a look at this blog post.  

Enjoy. 



No comments: