A user on Unix-like systems does not necessarily correspond to a human person who logs in and types commands, although it sometimes does. A user is simply a named entity that can own files and run commands with certain permissions and that may or may not have permission to read or modify other users' files. It's very common, for sound security reasons, to run each service on a system with its own user account. This simply means that the service runs with the identity and permissions of that user.
For example, a web server will often run as the www-data
user, which exists solely to own files the web server needs to read and write. This limits the danger of a security breach via the web server, because the attacker would only have the www-data
user's permissions, which are very limited, rather than the root
user's, which can modify any aspect of the system. It is generally a bad idea to run services exposed to the public Internet as the root
user. The service user should have only the minimum permissions it needs to operate the service.
Given this, an important part of system configuration involves creating and managing users, and Puppet's user
resource provides a model for doing just that. Just as we saw with packages and services, the details of implementation and the commands used to manage users vary widely from one operating system to another, but Puppet provides an abstraction which hides those details behind a common set of attributes for users.
The following example shows a typical user
and group
declaration in Puppet (user.pp
):
group { 'devs': ensure => present, gid => 3000, } user { 'hsing-hui': ensure => present, uid => '3001', home => '/home/hsing-hui', shell => '/bin/bash', groups => ['devs'], }
The title of the resource is the username (login name) of the user; in this example, hsing-hui
. The ensure => present
attribute says that the user should exist on the system.
The uid
attribute needs a little more explanation. On Unix-like systems, each user has an individual numerical id, known as the uid. The text name associated with the user is merely a convenience for those (mere humans, for example) who prefer strings to numbers. Access permissions are in fact based on the uid and not the username.
Why set the uid
attribute? Often, when creating users manually, we don't specify a uid, so the system will assign one automatically. The problem with this is that if you create the same user (hsing-hui
, for example) on three different nodes, you may end up with three different uids. This would be fine as long as you have never shared files between nodes, or copied data from one place to another. But in fact, this happens all the time, so it's important to make sure that a given user's uid is the same across all the nodes in your infrastructure. That's why we specify the uid
attribute in the Puppet manifest.
The home
attribute sets the user's home directory (this will be the current working directory when the user logs in, if she does log in, and also the default working directory for cron jobs that run as the user).
The shell
attribute specifies the command-line shell to run when the user logs in interactively. For humans, this will generally be a user shell, such as /bin/bash
or /bin/sh
. For service users, such as www-data
, the shell should be set to /usr/sbin/nologin
(on Ubuntu systems), which does not allow interactive access, and prints a message saying This account is currently not available
. All users who do not need to log in interactively should have the nologin
shell.
If the user needs to be a member of certain groups, you can pass the groups
attribute an array of the group names (just devs
in this example).
Although Puppet supports a password
attribute for user
resources, I don't advise you to use it. Service users don't need passwords, and interactive users should be logging in with SSH keys. In fact, you should configure SSH to disable password logins altogether (set PasswordAuthentication no
in sshd_config
).
The title of the resource is the name of the group (devs
). You need not specify a gid
attribute but, for the same reasons as the uid
attribute, it's a good idea to do so.
I like to have as few interactive logins as possible on production nodes, because it reduces the attack surface. Fortunately, with configuration management, it should rarely be necessary to actually log in to a node. The most common reasons for needing an interactive login are for system maintenance and troubleshooting, and for deployment. In both cases there should be a single account named for this specific purpose (for example, admin
or deploy
), and it should be configured with the SSH keys of any users or systems that need to log in to it.
Puppet provides the ssh_authorized_key
resource to control the SSH keys associated with a user account. The following example shows how to use ssh_authorized_key
to add an SSH key (mine, in this instance) to the ubuntu
user on our Vagrant VM (ssh_authorized_key.pp
):
ssh_authorized_key { '[email protected]': user => 'ubuntu', type => 'ssh-rsa', key => 'AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWACa2BzeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUawqzXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik=', }
The title of the resource is the SSH key comment, which reminds us who the key belongs to. The user
attribute specifies the user account which this key should be authorized for. The type
attribute identifies the SSH key type, usually ssh-rsa
or ssh-dss
. Finally, the key
attribute sets the key itself. When this manifest is applied, it adds the following to the ubuntu
user's authorized_keys
file:
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWACa2BzeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUawqzXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik= [email protected]
A user account can have multiple SSH keys associated with it, and anyone holding one of the corresponding private keys and its passphrase will be able to log in as that user.
If you need to have Puppet remove user accounts (for example, as part of an employee leaving process), it's not enough to simply remove the user
resource from the Puppet manifest. Puppet will ignore any users on the system that it doesn't know about, and it certainly will not remove anything it finds on the system that isn't mentioned in the Puppet manifest; that would be extremely undesirable (almost everything would be removed). So we need to retain the user
declaration for a while, but set the ensure
attribute to absent
(user_remove.pp
):
user { 'godot': ensure => absent, }
Once Puppet has run everywhere, you can remove the user
resource if you like, but it does no harm to simply leave it in place, and in fact, it's a good idea to do this, unless you can verify manually that the user has been deleted from every affected system.
If you need to prevent a user logging in, but want to retain the account and any files owned by the user, for archival or compliance purposes, you can set their shell
to /usr/sbin/nologin
. You can also remove any ssh_authorized_key
resources associated with their account, and set the purge_ssh_keys
attribute to true
on the user
resource. This will remove any authorized keys for the user that are not managed by Puppet.
3.128.170.92