Puppet

Managing Subversion w/vcsrepo with Non-root Remote Users


Goal

We wish to use vcsrepo in Puppet to check out a Subversion repository with a non-root remote user and custom SSH key pair.

Trouble

Despite the fact that vcsrepo happily accepts the user parameter (and does not complain about the identity parameter except in debug), it does not actually use either, so the svn command runs as root and uses root’s SSH keys.

Solution

All is not lost, however; Subversion allows for custom protocol tunnel schemes in ~/.subversion/config, which allow us to specify the SSH command, with which we can specify the remote user and identity file:

[tunnels]
ssh_user_<remote_user> = $SVN_SSH ssh -q -l <remote_user> -o IdentityFile=<ssh_identity_file>

Now when specifying the source parameter to the vcsrepo resource, we can use svn+ssh_user_<remote_user> as the protocol part of the repository URL.

If we want to keep this under the account of a different local user, we can use vcsrepo’s configuration parameter to point to ~/.subversion:

vcsrepo { '<local_repo>':
  ensure        => 'latest',
  provider      => 'svn',
  owner         => '<local_user>',
  group         => '<local_group>',
  user          => '<local_user>',
  source        => 'svn+ssh_user_<remote_user>://<svn_host>/<repo_path>',
  configuration => '/home/<local_user>/.subversion/config',
}

In Puppet

Since the ~/.subversion/config file is an ini-format file, we can use the ini_setting type from puppetlabs/inifile module to put it into place.

ini_setting { '/home/<local_user>/.subversion/config/tunnels/ssh_user_<remote_user>':
  path    =>  '/home/<local_user>/.subversion/config',
  section => 'tunnels',
  setting => 'ssh_user_svn',
  value   => '$SVN_SSH ssh -q -l svn -o IdentityFile=/home/<local_user>/.ssh/id_rsa',
}

We also want to ensure that the ini_setting is in place before the vcsrepo is applied. We could use either the require or before metaparameters or resource chaining, but if we have multiple vcsrepos, explicitly listing each one would be tedious; applying to all repos would also catch git repos, which is unnecessary. Instead, we can use resource collectors to select just the appropriate vcsrepo resources:

Ini_setting['/home/<local_user>/.subversion/config/tunnels/ssh_user_<remote_user>']
 -> Vcsrepo <| user == '<remote_user>' and provider == 'svn' |>

Migration

If the working copy already exists (for example, perhaps it was being managed with an exec resource), then the URL of the working copy will be different and vcsrepo won’t fix it. A simple svn switch --relocate with the repository root will fix it:

local_user$ svn info |grep Root
Repository Root: svn+ssh://<svn_host>/<repo_path>
$ svn switch --relocate 'svn+ssh://<svn_host>/<repo_path>' \
        'svn+ssh_user_<remote_user>://<svn_host>/<repo_path>'

Future

It would be nice if vcsrepo could support user directly and could use identity. I considered submitting a patch to do so, but it seems better to use the Puppet execution API but it does not support changing UIDs in Puppet 2.7 (which I lament to say I am currently still using). (Not to mention that this is the kind of thing that would be difficult to test.)