Caching of plaintext passwords by the Subversion client has been disabled by default

Introduction

In Subversion 1.12, the caching of plaintext passwords by the Subversion client has been disabled by default. This causes problems for cron jobs running svn update, etc. The specific commit that introduced this change is detailed here. It looks like a lot of people are discussing this, most eloquently Robby Zinchak wrote:

There are a tremendous number of scenarios where users would desire to use subversion without a keyring … I obviously can’t be logging into a keyring every time the server reboots, that’d be idiotic.

I cannot fathom how the [Subversion development] team thought this was a good decision. It reeks of devs thinking “we know better, force the users to do it this way,” without actually understanding the needs of your users.

This article discusses the problem and possible solutions in more detail.

My own use case

I have a script that performs such tasks as:

  • applying security updates
  • reverting manually made changes to critical files

I would install this script on new systems by calling the following command interactively:

svn co <https-based-url> <working-copy>

Each night, the script was executed by cron calling the following commands indirectly and non-interactively:

cd <working-copy>
svn update
<run-the-script>

Obviously that svn update relies heavily on password-less access to the Subversion repository.

The old behaviour

In Debian 10, which contains Subversion 1.10.4, svn --version reports:

pestaroli# svn --version
...
The following authentication credential caches are available:

* Plaintext cache in /root/.subversion
* Gnome Keyring
* GPG-Agent
* KWallet (KDE)

pestaroli#

and that initial checkout went like this:

pestaroli# cd /etc/pcms
pestaroli# svn co https://svn.pasta.freemyip.com/private/pcms-config/trunk pcms-config
Authentication realm: <https://svn.pasta.freemyip.com:443> Subversion Service
Password for 'root': 

Authentication realm: <https://svn.pasta.freemyip.com:443> Subversion Service
Username: alexis
Password for 'alexis': ********************************


-----------------------------------------------------------------------
ATTENTION!  Your password for authentication realm:

   <https://svn.pasta.freemyip.com:443> Subversion Service

can only be stored to disk unencrypted!  You are advised to configure
your system so that Subversion can store passwords encrypted, if
possible.  See the documentation for details.

You can avoid future appearances of this warning by setting the value
of the 'store-plaintext-passwords' option to either 'yes' or 'no' in
'/root/.subversion/servers'.
-----------------------------------------------------------------------
Store password unencrypted (yes/no)? yes
A pcms-config/doc
A pcms-config/files
A pcms-config/files/etc
...

Note the messages Plaintext cache in /root/.subversion and Store password unencrypted (yes/no)?, which ensure that when the cron job runs svn update – which I simulate here by running it myself -, it does not ask for a password:

pestaroli# cd /etc/pcms/pcms-config
pestaroli# svn update
Updating '.':
At revision 1237.
pestaroli#

This behaviour is exactly what I need for a nightly cron job.

The new behaviour

However, in Debian 11 (not yet released at the time of writing), which contains Subversion 1.14.1, svn --version reports:

testaroli# svn --version
...
The following authentication credential caches are available:

* Gnome Keyring
* GPG-Agent
* KWallet (KDE)

testaroli#

and that initial checkout went like this:

testaroli# cd /etc/pcms
testaroli# svn co https://svn.pasta.freemyip.com/private/pcms-config/trunk pcms-config
Authentication realm: <https://svn.pasta.freemyip.com:443> Subversion Service
Password for 'root': 

Authentication realm: <https://svn.pasta.freemyip.com:443> Subversion Service
Username: alexis
Password for 'alexis': ********************************

A    pcms-config/doc
A    pcms-config/files
A    pcms-config/files/etc
...

Note the absence of the messages Plaintext cache in /root/.subversion and Store password unencrypted (yes/no)?, the consequence of which is that when the cron job runs svn update, it does ask for a password:

testaroli# svn update
Updating '.':
Authentication realm: <https://svn.pasta.freemyip.com:443> Subversion Service
Password for 'alexis':

This is obviously no good for a cron job.

Alternatives and workarounds

In his post (link above) Robby Zinchak listed a few options, which I summarise here:

  1. configure or compile gnome-keyring or kwallet in such a way that they themselves do not need a master password to be entered and then allow them to store the Subversion password
  2. compile and package Subversion yourself starting with:
    ./configure --enable-plaintext-password-storage ...
    
  3. use somebody else’s Subversion package that was prepared in the same way
  4. switch to Git

I rejected all of these options because I cannot afford the time that being a package maintainer would require and out of loyalty to Subversion, which I’ve been using for a long time.

However, there are a few more options:

  1. switch to svn+ssh://
  2. use an svn wrapper that specifies the password
  3. manually insert passwords in ~/.subversion

These options are discussed in more detail below.

Switching to svn+ssh://

The official documentation describes how to use svnserve over an ssh tunnel, which relieves svnserve of the task of authenticating clients. In combination with an ssh public key pair in which the private key is itself not encrypted, this appeared to be a viable option.

However, I had some requirements that complicated this option and ultimately led me to reject it:

  • anonymous access via https (this is required to allow me to put links to scripts stored in Subversion into my web pages like this)
  • authenticated read-only and authenticated read-write access via any protocol that supports not entering a password (as required for my overnight cron jobs)
  • URLs should always be of the following form, regardless whether the client is inside or outside my home network:
    {svn+ssh|https}://svn.pasta.freemyip.com/<repository-name>/<path>

    (this requirement is here in order to ensure that the URLs are intuitive and easy to recall)

  • avoid using svnserve.conf for authentication and authorisation (it fragments these things too much)

Although, in the end, I rejected this option (because the final URL format was so ugly), it may be useful for other people to know more details.

The communications architecture was to be as follows:

Note that:

  • red and green lines represent DNS and Subversion requests by an external Subversion client over https:// and svn+ssh://, respectively; replies are omitted to avoid clutter
  • blue and orange lines represent DNS and Subversion requests by an internal Subversion client over https:// and svn+ssh://, respectively
  • the external DNS server resolves the Subversion server’s IP address to the address of my router on the internet
  • the internal DNS server uses RPZ records to overrule that and resolve it to the address of my front-end webserver (details of making bind9 do this can be found here)
  • my home router routes new connections from the internet according to their destination port number; in order that svn+ssh:// connections do not follow the route of interactive ssh connections, I dedicate the unused port number 24801 for svn+ssh:// traffic
  • the front-end webserver’s Apache process proxies https requests for the Subversion server vhost to the back-end webserver’s Apache process
  • the front-end webserver runs a socat process to “proxy” svn+ssh:// requests to the back-end webserver’s sshd process (this is to ensure that – when using clients on the home network only -changing the protocol in the URL does not also necessitate changing the host in the URL)

Regarding authentication and authorisation:

  • each repository belongs to a dedicated per-repository user, for example:
    pestaroli# grep main /etc/passwd
    main:x:996:996::/var/local/repoadmin/ssh/main:/bin/bash
    pestaroli# grep main /etc/shadow
    main:*:18817::::::
    pestaroli# ls -l /var/local/repoadmin/repos/main/
    total 24
    -rw-r--r-- 1 main main  246 Jul 9 12:34 README.txt
    drwxr-sr-x 2 main main 4096 Jul 9 12:34 conf
    drwxr-sr-x 6 main main 4096 Jul 9 12:34 db
    -r--r--r-- 1 main main    2 Jul 9 12:34 format
    drwxr-sr-x 2 main main 4096 Jul 9 12:34 hooks
    drwxr-sr-x 2 main main 4096 Jul 9 12:34 locks
    pestaroli#
  • https:// is for anonymous access only
  • svn+ssh:// is for authenticated access only
  • users are authenticated and granted read-only or read-write access according to entries in each repository owner’s authorized_keys file, for example:
    pestaroli# cat ~main/.ssh/authorized_keys 
    command="svnserve --tunnel --tunnel-user=alice --root=/var/local/repoadmin/repos/main" ssh-rsa AAAA...fN0P alexis@pasta.net
    command="svnserve --tunnel --tunnel-user=bob --read-only --root=/var/local/repoadmin/repos/main" ssh-dss AAAA...5B2g= suzie@lasagne
    pestaroli#

As mentioned above, I rejected this approach because the URL format for svn+ssh:// access got compromised; it became this:

https://<repository-name>@svn.pasta.freemyip.com/<path>@

Note that:

  • the relocation of <repository-name> from after the hostname to before it
  • the second @ sign, which is needed to stop the svn client interpreting the first @ sign as indicating a pegged revision.

That’s pretty ugly, which is why I rejected this option.

using an svn wrapper

A user can add the following to ~/.bashrc (or equivalent):

alias svn='svn --password=xxx'

in order to specify the password in each call to svn.

Scripts and other programs typically don’t load aliases but svn can still be replaced with a small wrapper script. Firstly, the old svn command needs to be renamed:

dpkg-divert --divert /usr/bin/svn.real --rename /usr/bin/svn

and then a wrapper script needs to be created:

echo -e '#!/bin/bash\nexec /usr/bin/svn.real --password=xxx "$@"' > /usr/bin/svn
chmod 755 /usr/bin/svn

The main problems with this option are that it only supports one password, which probably means it only supports one user, and that the password is revealed to all users. Okay, this could be somewhat improved: the wrapper script could read the password out of a dot file in the caller’s home directory, but each user is restricted to one password.

manually inserting passwords in ~/.subversion

From the output in the section ‘The new behaviour’ above, it should be clear that, although the password is not cached, the username is cached!

On the Subversion users mail list, Nathan Hartman says:

However, Subversion will _use_ the password, if it is already stored on disk.

So, if we can find a way to write the password into ~/.subversion, we should get close enough to the old behaviour.

The Subversion FAQ says:

In response to various questions and requests, the Subversion developers have written a Python script that can store a plain-text password to the cache. If you understand the security implications, have ruled out other alternatives, and still want to cache your password in plain-text on disk, you may find the script here:

TODO: Link to the script.

Yes, it really does say “TODO: Link to the script” 🙁 But all is not lost! Adding the password is trivial! 🙂

  1. Find the authorisation file that has cached the username but not the password by running:
    find ~/.subversion/auth/ -type f

    If more then one file is listed, then you will need to examine the files to find the right one; these files are very short text files, so you can examine them with cat.

  2. Edit the file and add these lines at the beginning of the file:
    K 8
    passtype
    V 6
    simple
    K 8
    password
    V 3
    XXX

    (You can add these lines at the end of the file but they must go immediately before the END text; so adding the text at the beginning of the file is easier.)

  3. Edit the file again and replace the XXX with your password and the 3 with the length of your password.

Alternatively, you can try the zsh script, which Danial Shafaf wrote and posted in the thread that Robby Zinchak started.

Alternatively, you can try this simple bash script, which I wrote; feel free to mail me corrections.

Conclusion

From the methods listed above, I chose to manually insert the password into ~/.subversion because it was the cleanest and least disruptive option.

However, if the Subversion developers have already decided to disable support for plain text password stores by default, then it is feasible that in the future they might decide to remove support for plain text password stores altogether. As such, I think this story has not reached its end; in which case, switching to Git would be my preferred option.

See also