[[TOC]]
This document explains how to create new shell (and email) accounts. See also doc/accounts to evaluate new account requests.
Note that this documentation needs work, as it overlaps with user-facing user management procedures (doc/accounts), see issue 40129.
Configuration
This should be done only once.
git clone db.torproject.org:/srv/db.torproject.org/keyrings/keyring.git account-keyring
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.
When cloning, you may get the following message (see tpo/tpa/team#41785):
fatal: detected dubious ownership in repository at '/srv/db.torproject.org/keyrings/keyring.git'
If this happens, you need to run the following command as your user on
db.torproject.org:
git config --global --add safe.directory /srv/db.torproject.org/keyrings/keyring.git
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 requester, as detailed in doc/accounts.
The short version is:
-
Import the provided key to your keyring. That is necessary for the script in the next point to work.
-
Verify the provided OpenPGP key
It should be signed by a trusted key in the keyring or in a message signed by a trusted key. See doc/accounts when unsure.
-
Add the OpenPGP key to the
account-keyring.gitrepository and create the LDAP account:FINGERPRINT=0123456789ABCDEF0123456789ABCDEF01234567 && NEW_USER=alice && REQUESTER="bob in ticket #..." && ./NEW "$FINGERPRINT" "$NEW_USER" && git add torproject-keyring/"${NEW_USER}-${FINGERPRINT}.gpg" && git commit -m"new user ${NEW_USER} requested by ${REQUESTER}" && git push && ssh -tt $USER@alberti.torproject.org "ud-useradd -n && sudo -u sshdist ud-generate && sudo -H ud-replicate"
The last line will create the user on the LDAP server. See below for detailed information on that magic instruction line, including troubleshooting.
Note that $USER, in the above, shouldn't be explicitly expanded
unless your local user is different from your alberti user. In my
case, $USER, locally, is anarcat and that is how I login to
alberti as well.
Notice that when prompted for whom to add (a GPG search), enter the
full $FINGERPRINT verified above
What followed are detailed, step-by-step instructions, to be performed
after the key was added to the account-keyring.git repository (up
to the git push step above).
on the LDAP server
Those instructions are a copy of the last step of the above instructions, provided to clarify what each step does. Do not follow this procedure and instead follow the above.
The LDAP server is currently alberti. Those steps are supposed to be
ran as a regular user with LDAP write access.
-
create the user:
ud-useradd -nThis 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$FINGERPRINTverified 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 theEmail forwarding addressif present and remove it from Puppet
-
-
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.
-
synchronize the change:
sudo -H ud-replicate -
run puppet:
sudo puppet agent -t
Creating a user without a PGP key
In most cases we want to use the person's PGP key to associate with their new LDAP account, but in some cases it may be difficult to get a person to generate a PGP key (and most importantly, keep managing that key effectively afterwards) and we might still want to grant the person an email account.
For those cases, it's possible to create an LDAP account without associating it to a PGP key.
First, generate a password and note it down somewhere safe temporarily. Then generate a hash for that password and noted it down. If you don't have this command on your computer, you can run that on alberti:
mkpasswd -m bcrypt-a
On alberti, find a free user ID with fab user.list-gaps (more information on
that command in the creating a role section)
Then, on alberti, connect to ldapvi and at the end of the file add something
like the following. Make sure to modify uid=[...] and all UID and GID numbers
and then the user's cn and sn fields to values that make sense for your case
and replace the value of mailPassword with the password hash you noted down
earlier. Keep the userPassword as-is since it will tell LDAP to lock the LDAP
account:
add gid=exampleuser,ou=users,dc=torproject,dc=org
gid: exampleuser
gidNumber: 15xx
objectClass: top
objectClass: debianGroup
add uid=exampleuser,ou=users,dc=torproject,dc=org
uid: exampleuser
objectClass: top
objectClass: inetOrgPerson
objectClass: debianAccount
objectClass: shadowAccount
objectClass: debianDeveloper
uidNumber: 15xx
gidNumber: 15xx
gecos: exampleuser,,,,
cn: Example
sn: User
userPassword: {crypt}$LK$
mailPassword: <REDACTED>
loginShell: /bin/bash
mailCallout: FALSE
mailContentInspectionAction: reject
mailGreylisting: FALSE
mailDefaultOptions: FALSE
Save and exit and you should get prompted about adding two entries.
Lastly, refresh and resync the user database:
- On alberti:
sudo -u sshdist ud-generate && sudo -H ud-replicate - On submit-01 as root:
ud-replicate
The final step is then to contact the person on Signal and send them the password in a disappearing message.
troubleshooting
If the ud-useradd command fails with this horrible backtrace:
Updating LDAP directory..Traceback (most recent call last):
File "/usr/bin/ud-useradd", line 360, in <module>
lc.add_s(Dn, Details)
File "/usr/lib/python3/dist-packages/ldap/ldapobject.py", line 236, in add_s
return self.add_ext_s(dn,modlist,None,None)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/ldap/ldapobject.py", line 222, in add_ext_s
resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/ldap/ldapobject.py", line 543, in result3
resp_type, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4(
^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/ldap/ldapobject.py", line 553, in result4
ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/ldap/ldapobject.py", line 128, in _ldap_call
result = func(*args,**kwargs)
^^^^^^^^^^^^^^^^^^^^
ldap.INVALID_SYNTAX: {'msgtype': 105, 'msgid': 6, 'result': 21, 'desc': 'Invalid syntax', 'ctrls': [], 'info': 'sn: value #0 invalid per syntax'}
... it's because you didn't fill the form properly. In this case, the
sn field ("Last name" in the form) was empty. If you don't have a
second name, just reuse the first name.
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:
-
Do not use
ud-groupaddandud-roleadd. They are partly broken. -
Run
fab user.list-gapsfrom a clone of thefabric-tasksrepository on alberti.tpo to find an unuseduidNumber/gidNumberpair. - Make sure the numbers match. If you are unsure, find the highest
uidNumber/gidNumberpair, increment that and use it as a number. You must absolutely make sure the number is not already in use. -
the fabric task connects directly to ldap, which is firewalled from the exterior, so you won't be able to run the task from your computer.
-
On LDAP host (currently alberti.tpo), as a user with LDAP write access, do:
ldapvi -ZZ --encoding=ASCII --ldap-conf -h db.torproject.org -D uid=${USER},ou=users,dc=torproject,dc=org -
Create a new
grouprole for the new account: -
Copy-paste a previous
gidthat is also adebianGroup - Change the first word of the copy-pasted block to
addinstead of the integer - Change the
cn(first line) to the new group name - Change the
gid:field (last line) to the new group name -
Set the
gidNumberto the number found in step 2 -
Create the actual
userrole: -
Copy-paste a previous
uidrole entry (with aobjectClass: debianRoleAccount). - Change the first word of the copy-pasted block to
addinstead of the integer - Change the
uid=,uid:,gecos:andcn:lines. - Set the
gidNumberanduidNumberto the number found in step 2 -
If you need to set a mail password you can generate a blowcrypt password with python (search for example of how to do this). Change the hash identifier to
$2y$instead of$2b$. -
Add the role to the right host:
-
Add a
allowedGroups: NEW-GROUPline to host entries that should have this role account deployed. -
If the role account will only be used for sending out email by connecting to submission.torproject.org, the account does not need to be added to a host.
-
Save the file, and accept the changes
-
propagate the changes from the LDAP host:
sudo -u sshdist ud-generate && sudo -H ud-replicate -
(sometimes) create the home directory on the server, in Puppet:
file { '/home/bridgescan': ensure => 'directory', mode => '0755', owner => 'bridgescan', group => 'bridgescan'; }
Sometimes a role account is made to start services, see the doc/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 sudo configuration that enable us to give piecemeal accesses
like this. We often give accesses to groups instead of specific
users for easier maintenance.
Entries should be added by declaring a sudo::conf resource in the
relevant profile class in Puppet. For example:
sudo::conf { 'onbasca':
content => @(EOT)
# This file is managed by Puppet.
%onbasca ALL=(onbasca) ALL
| EOT
}
An alternative to this which avoids the need to create a profile class
containing a single sudo::conf resource is to add the configuration to
Hiera data. The equivalent for the above would be placing this YAML
snippet at the role (preferably) or node hierarchy:
profile::sudo::configs:
onbasca:
content: |
# This file is managed by Puppet.
%onbasca ALL=(onbasca) ALL
Sudo primer
As a reminder, the sudoers file syntax can be distilled to this:
FROMWHO HOST=(TOWHO) COMMAND
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 onionperf.torproject.org, /usr/local/bin/static-update-component onionperf.torproject.org
Update a user's GPG key
The account-keyring repository contains an update script ./UPDATE which takes
the ldap username as argument and automatically updates the key.
If you /change/ a user's key (to a new primary key), you also need to update
the user's keyFingerPrint attribute in LDAP.
After updating a key in the repository, the changes must be pushed to the remote hosted on the LDAP server.
Other documentation
Note that a lot more documentation about how to manage users is available in the LDAP documentation.