This document explains how to create new shell (and email) accounts. See also accounts to evaluate new account requests.


This should be done only once.

git clone
git -C account-keyring remote add alberti

It downloads the git repository that manages the OpenPGP keyring. This keyring is essential as it allows users to interact with the LDAP database securely to perform password changes and is also used to send the initial password for new accounts.

Creating a new user

This procedure can be used to create a real account for a human being. If this is for a machine or another automated thing, create a role account (see below).

To create a new user, specific information need to be provided by the requestee, as detailed in accounts.

The short version is:

FINGERPRINT=0123456789ABCDEF0123456789ABCDEF01234567 &&
NEW_USER=alice &&
git add torproject-keyring/"${NEW_USER}-${FINGERPRINT}.gpg" &&
git commit -m"new user ${NEW_USER} requested by ${REQUESTOR}" &&
git push &&
git push alberti &&
ssh -tt $ "ud-useradd -n && sudo -u sshdist ud-generate && sudo -H ud-replicate"

See below for detailed instructions.

on your own machine

For example, your laptop.

  1. verify the OpenPGP key provided

    It should be signed by a trusted key in the keyring or in a message signed by a trusted key. See accounts when unsure.

  2. add pgp key to the account-keyring repository:

    git add torproject-keyring/"${NEW_USER}-${FINGERPRINT}.gpg" &&
    git commit -m"new user ${NEW_USER} requested by ${REQUESTOR}"
  3. push to both repositories:

    git push &&
    git push alberti

on the LDAP server

This is currently alberti. Make sure you run as a regular user with LDAP write access.

  1. create the user:

    ud-useradd -n

    This command asks a bunch of questions interactively that have good defaults, mostly taken from the OpenPGP key material, but it's important to review them anyways. in particular:

    • when prompted for whom to add (a GPG search), enter the full $FINGERPRINT verified above

    • the email forward is likely to be incorrect if the key has multiple email address as UIDs

    • the user might already be present in the Postfix alias file (tor-puppet/modules/postfix/files/virtual) - in that case, use that email as the Email forwarding address if present and remove it from Puppet

    • if the user is a "guest" (ie. it needs to have access only to a subset of machines), you should use the -g flag to ud-useradd. this will put the user in the guest group. it will also prompt for a list of allowed machines (which can be left empty) and an expiry date for the account (which can be set to zero to disable). then the group can be changed with ldapvi.

  2. synchronize the change:

     sudo -u sshdist ud-generate && sudo -H ud-replicate

on other servers

This step is optional and can be used to force replication of the change to another server manually. In this case, we force the change to propagate to the email server, which is currently eugeni.

  1. synchronize the change:

    sudo -H ud-replicate
  2. verify the email alias was correctly created:

    egrep -q "${NEW_USER}" /etc/postfix/debian || echo "new user missing, please fix"
  3. run puppet:

    sudo puppet agent -t

Creating a role

A "role" account is like a normal user, except it's for machines or services, not real people. It's useful to run different services with different privileges and isolation.

Here's how to create a role account:

  1. Do not use ud-groupadd and ud-roleadd. They are partly broken.

  2. On LDAP host (currently alberti.tpo), as a user with LDAP write access, do:

    ldapvi -ZZ --encoding=ASCII --ldap-conf -h -D uid=${USER},ou=users,dc=torproject,dc=org
  3. Create a new group role for the new account:

    • Copy-paste a previous gid that is also a debianGroup
    • Change the first word of the copy-pasted block to add instead of the integer
    • Bump the gidNumber to the latest gidNumber in the file plus one
  4. Create the actual user role:

    • Copy-paste a previous uid role entry (with a objectClass: debianRoleAccount).
    • Change the first word of the copy-pasted block to add instead of the integer
    • Change the uid=, uid:, gecos: and cn: lines.
    • Bump the uidNumber to the latest uidNumber in the file plus one.
    • Use the same gidNumber as the one generated in the first step.
  5. Add the role to the right host:

    • Add a allowedGroups: NEW-GROUP line to host entries that should have this role account deployed.
  6. Save the file, and accept the changes

  7. propagate the changes from the LDAP host:

    sudo -u sshdist ud-generate && sudo -H ud-replicate
  8. (sometimes) create the home directory on the server, in Puppet:

     file {
         ensure => 'directory',
         mode   => '0755',
         owner  => 'bridgescan',
         group  => 'bridgescan';

Sometimes a role account is made to start services, see the services page for instructions on how to do that.

Sudo configuration

A user will often need to more permissions than its regular scope. For example, a user might need to be able to access a specific role account, as above, or run certain commands as root.

We have a large sudoers file that contains per-host configuration snippets that enable us to give piecemeal accesses like this. We often give accesses to groups instead of specific users for easier maintenance.

Sudo primer

As a reminder, the sudoers file syntax can be distilled to this:


For example, this allows the group wheel (FROMWHO) to run the service apache reload COMMAND as root (TOWHO) on the HOST example:

%wheel example=(root) service apache reload

The HOST, TOWHO and COMMAND entries can be set to ALL. Aliases can also be defined and many more keywords. In particular, the NOPASSWD: prefix before a COMMAND will allow users to sudo without entering their password.

Granting access to a role account

That being said, you can simply grant access to a role account by adding users in the role account's group (through LDAP) then adding a line like this in the sudoers file:

%roleGroup example=(roleAccount) ALL

Multiple role accounts can be specified. This is a real-world example of the users in the bridgedb group having full access to the bridgedb and bridgescan user accounts:

%bridgedb       polyanthum=(bridgedb,bridgescan)            ALL

Another real-world example, where members of the %metrics group can run two different commands, without password, on the STATICMASTER group of machines, as the mirroradm user:

%metrics        STATICMASTER=(mirroradm)    NOPASSWD: /usr/local/bin/static-master-update-component, /usr/local/bin/static-update-component