User authentication at Savannah

User account creation

  1. Anyone can register a Savannah account using the web interface: https://savannah.gnu.org/account/register.php
  2. These accounts are used across all Savannah systems.
  3. Users can upload ssh public keys using the web interface at: https://savannah.gnu.org/my/admin/editsshkeys.php
  4. ssh public keys are stored in the database on internal0.savannah.gnu.org (see SavannahServices).

User information can be viewed by anyone on Savannah website. Example for user agn: https://sv.gnu.org/users/agn/

Database access

In the database, currently internal0.savannah.gnu.org but accessible from a set of hosts, the following SQL commands can be used to examine accounts:

$ echo "select
          user_id, user_name, email, realname,
          uidNumber, authorized_keys
        from user
        where user_name = 'agn'" \
      | mysql savane

+---------+-----------+----------+--------------+-----------+-----------------+
| user_id | user_name | email    | realname     | uidNumber | authorized_keys |
+---------+-----------+----------+--------------+-----------+-----------------+
|   94790 | agn       | [email]  | Assaf Gordon |    131035 | ssh-rsa AAAAB3Nz|
+---------+-----------+----------+--------------+-----------+-----------------+

The authorized_keys field contains all user's SSH public keys, concatenated with a ### delimiter, as a one-line string. Users upload their GPG keys, and they are stored in the database. Some users have uploaded malformed and invalid GPG key information. Therefore some of these records are invalid due to having invalid information uploaded.

Activating accounts

This is done via the web siteadmin interface: Main page -> Browse Users List -> find user -> [Activate] in the Actions column.

Groups and accounts

In Savannah systems, there is a Unix user for each Savannah registered account:

 vcs0:~# getent passwd agn
 agn:x:131035:1003:Assaf Gordon:/srv:/usr/local/bin/sv_membersh

and a unix group for each Savannah registered group:

vcs0:~# getent group datamash
datamash:x:77800:agn

Access control is based on Unix group membership.

Example:

The gawk (GNU Awk) group (http://sv.gnu.org/p/gawk) has six members as of Nov. 2014 (http://sv.gnu.org/project/memberlist.php?group=gawk).

The git repository on vcs.sv.gnu.org belongs in the gawk group:

vcs0:~# ls -ld /srv/git/gawk.git/
drwxrwsr-x 8 root gawk 4096 Nov  4 01:23 /srv/git/gawk.git/

The members of the gawk group are allowed to push code updates to the gawk repository:

vcs0:~# getent group gawk
gawk:x:6731:ajschorr,arnold,eliz,jkahrs,scldad,wb8tyw

Authentication mechanisms

For VCS repositories (git/hg/bzr/svn/cvs on vcs.sv.gnu.org) and download area (on dl.sv.gnu.org), users are authenticated using SSH access and their public keys. See SavannahServices for details about the available services on these servers.

Savannah users who are not members of any group (i.e., do not have write access to any repository) do not have ssh login access on vcs even if they have set up their ssh keys. getent passwd USER will return empty results for such users, even if they are valid users in the database (i.e., they exist in the users mysql table).

Technically, this is due to the libnss-mysql (see below) script doing an SQL JOIN on the user and user_group tables, and requiring having at least one record in the user_group table. Maybe someday this will be changed.

These servers use the database in two ways:

  • Unix user management, using nsswitch and libnss-mysql.
  • ssh key authentication, using the custom AuthorizedKeysCommand option.

nsswitch and libnss-mysql

The files download0:/etc/nsswitch.conf and vcs0:/etc/nsswitch.conf contain the following configuration:

...
passwd:         compat mysql
group:          compat mysql
shadow:         compat mysql
...

Bob Proulx explains:

That is how libc is configured. 'compat' means /etc/passwd in the normal compatible way. 'mysql' means if not found in the first compat section then look it up in mysql. That is what allows libc to find users in the mysql database.

 download0:~# getent passwd agn
 agn:x:131035:1003:Assaf Gordon:/srv:/usr/local/bin/sv_membersh

The SQL statements to extract information from the database on internal0 are defined in download0:/etc/libnss-mysql.cfg and vcs0:/etc/libnss-mysql.cfg.

uidNumber

Savannah users have two IDs in the database: user.user_id and user.uidNumber. user_id is used in the PHP frontend alone (it is the primary key for the user table). uidNumber is the user's unix account user ID (used by getent above).

When a new user is registerd on Savannah, the user_id will be unique, but the uidNumber will be NULL.

A cron job (mgt1:/etc/cron.d/savannah) calls a script (sv_assign_uid_gid) which scans the new users in the database, and creates unix user IDs and group IDs if needed (if users are not part of any group, they won't need ssh access and thus won't need user ID).

Search for sv_assign_uid_gid in SavannahInternals to see how it is called (but note that the page is outdated: the script now runs as a cron job on mgt1, not internal0).

ssh authentication

The file /etc/ssh/sshd_config on download0: and vcs0: servers have the line:

...
AuthorizedKeysCommand      /opt/savane/bin/sv_get_authorized_keys
...

When users log in to Savannah servers using ssh, they specify the account:

git clone agn@git.sv.gnu.org:/srv/git/datamash.git

The user is therefore known, and OpenSSH needs to find user's public keys. The sv_get_authorized_keys Perl script simply queries the ssh public keys of the user (splitting them by ### delimiter):

...
my ($authorized_keys) = $dbd->selectrow_array(q[
    SELECT authorized_keys
    FROM user
    WHERE user_name = ?], undef, $user);

print join("\n", split('###', $authorized_keys));
...

Manually invoking sv_get_authorized_keys looks like:

vcs0:~# /opt/savane/bin/sv_get_authorized_keys agn
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvs [...]
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ [...]

To validate user's keys on vcs, use the following script:

vcs0:~# ~/bin/check-user-pubkeys.sh agn
gordon@host1     2048 fe:61:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /tmp/pubkey.check.9WvGnA/key.00 (RSA)
gordon@host2     2048 87:21:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /tmp/pubkey.check.9WvGnA/key.01 (RSA)
gordon@host3     2048 3d:00:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /tmp/pubkey.check.9WvGnA/key.02 (RSA)

vcs0:~# ~/bin/check-user-pubkeys.sh someuser
someuser@host1   4096 6b:36:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /tmp/pubkey.check.DmNCzP/key.00 (RSA)
someuser@host2   4096 0b:c3:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /tmp/pubkey.check.DmNCzP/key.01 (RSA)
Invalid key: /tmp/pubkey.check.DmNCzP/key.02
Errors found in ssh pubkeys for user 'someuser'.
to troubleshoot, check files in /tmp/pubkey.check.DmNCzP .

The above script can help with troubleshooting user's SSH login problems.

mgt and root access

mgt1.savannah.gnu.org is the management server (see SavannahArchitecture for more details).

root access to mgt1 (and from there to the other Savannah servers) is controlled by mgt1:/root/.ssh/authorized_keys. This file is updated manually by existing Savannah administrators, adding ssh public keys of authorized Savannah hackers.

ssh access to mgt1 is only possible from fencepost (and other internal FSF machines to which no one outside the FSF has access). ssh access to vcs0 and download0 is open to all as required by their respective functions.

fencepost

fencepost.gnu.org is the general-purpose server for GNU hackers (for more information: https://www.gnu.org/software/README.accounts.html).

It is managed by FSF sysadmin, and not managed by Savannah people in any way whatsoever.

GNU webpage repository access for www members

Members of the www group (= GNU webmasters) have write access to the webpage repository for all gnu (not nongnu) groups. This is accomplished with ACLs. See ChangeLog entries by Beuc for 2006-06-28 and 2006-05-10, recorded in http://lists.gnu.org/archive/html/savannah-hackers-public/2016-05/msg00031.html. The key command is:

setfacl -m group:www:rw- FILES
setfacl -m default:group:www:rwx -m group:www:rwx DIRS

To set all of them I think this is needed. Testing it now.

setfacl -m group:www:rw- group/CVSROOT/history
find group/group -type d -exec setfacl -m default:group:www:rwx -m group:www:rwx {} +
find . -name CVSROOT -prune -o -type f -exec setfacl -m group:www:rw- {} +

The Cvs.pm Savane module installs this access for new gnu groups.