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:
- 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
- compile and package Subversion yourself starting with:
./configure --enable-plaintext-password-storage ...
- use somebody else’s Subversion package that was prepared in the same way
- 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:
- switch to svn+ssh://
- use an svn wrapper that specifies the password
- 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! 🙂
- 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
. - 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.) - Edit the file again and replace the
XXX
with your password and the3
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.