Subversion Authentication is Painful. Let's Fix That.
Subversion authentication is a pain. For years, I got by on plain text storage, but that's insecure and deprecated. On some platforms, like Mac OS, it's possible to use the system keychain to manage access. However, this becomes both a source of inconsistency and frustration as I work across multiple devices and operating systems.
What I wanted was to have subversion pull my credentials out of 1password. But how would I even begin to add this support to subversion?
Inspired by the 1password command line tool and this recipe for managing ssh keys in 1password, I found a way. This post is my attempt to document that process.
Assemble the Pieces
Convincing subversion to use 1password for authentication requires three properly configured components. If any of the three tools isn't setup right, the chain fails and authentication is a no go. Let's go through this step by step.
Tool 1: op
The op command line tool gives you shell script access to 1password. Make sure you can sign in and pull down the password for the relevant subversion accounts. Here's how that may look:
# The First Time $ op signin allmysecrets.1password.com contact.ben.simon@gmail.com # After the account is setup $ eval $(op signin allmysecrets) # Print the password for your subversion account. $ op get item 'master svn account' | \ jq -r '.details.fields[] | select(.designation=="password").value'
Tool 2: gpg-agent
gpg-agent is the glue that holds our solution together. Subversion, as we'll see in a moment, can be convinced to consult gpg-agent for credentials. Using gpg-agent's ability to preset a passphrase, it's possible to programmatically insert credentials into gpg-agent. To configure this, make sure your gpg-agent config file, ~/.gnupg/gpg-agent.conf, has the following settings:
allow-loopback-pinentry allow-preset-passphrase default-cache-ttl 34560000 max-cache-ttl 34560000
The allow-preset-passphrase setting is key. It's what allows gpg-agent to accept passphrases from an external source like 1password. The high values for default-cache-ttl and max-cache-ttl ensure that once I store credentials in gpg-agent they won't time out. This is personal preference, and if you wished, you could lower this value.
Having a properly configured gpg-agent gets you most of the way there. The last challenge to setting up gpg-agent is finding the location of the gpg-preset-passphrase command on your system. In every version of Linux I use, I find it's in a different location. For example, I'm composing this blog post on a Windows Subsystem for Linux Ubuntu instance, and gpg-preset-passphrase is found in /usr/lib/gnupg/.
With the config setup and gpg-preset-passphrase found, it's time to try this out.
# Confirming our test creds aren't there. $ echo "GET_PASSPHRASE --no-ask --data passphrasetest1 a b c" | gpg-connect-agent ERR 67108922 No data <GPG Agent> # Store the credentials $ /usr/lib/gnupg/gpg-preset-passphrase -c -P "ShhhItsASecret" passphrasetest1 # Confirm they are stored $ echo "GET_PASSPHRASE --no-ask --data passphrasetest1 a b c" | gpg-connect-agent D ShhhItsASecret OK
Tool 3: subversion
First off, your version of subversion needs to be built with gpg-agent support. Check this by running svn --version:
$ svn --version svn, version 1.13.0 (r1867053) compiled Mar 24 2020, 12:33:36 on x86_64-pc-linux-gnu ... The following authentication credential caches are available: * Gnome Keyring * GPG-Agent * KWallet (KDE)
If GPG-Agent isn't listed under available authentication credential caches then you need to build or download a version of subversion that does have this support. It'll be worth it, I promise.
Next, update ~/.subversion/config so that it uses the gpg-agent authentication cache:
### Section for authentication and authorization customizations. [auth] ### Set password stores used by Subversion. They should be ### delimited by spaces or commas. The order of values determines ... # password-stores = gpg-agent,gnome-keyring,kwallet ### To disable all password stores, use an empty list: password-store = gpg-agent
Next, perform an operation that requires subversion authentication, like say 'svn up.' If all goes well, svn should prompt you for your password by using your system's gpg-agent pin-entry program. You can cancel out of this.
Finally, determine a number of important details with how subversion is interacting with gpg-agent. Do this by looking in the subversion auth directory, ~/.subversion/auth/svn.simple/. You should see files named like so:
$ cd ~/.subversion/auth/svn.simple $ ls -1 3f08d77847ea42f0b7b1ccd66fd14138 a7c22b9f8eabdc93df040f5954967d2b 7f0a2d6c2e00dc758c9385fe821f07cb
There should be one file for each subversion domain that you access. Peeking inside one of these files should show you something like the following:
$ cat 3f08d77847ea42f0b7b1ccd66fd14138 K 8 passtype V 9 gpg-agent K 15 svn:realmstring V 39 <https://master.securerepo.com:443> SVN K 8 username V 3 dev END
This config file tells subversion that whenever it wishes to access repositories hosted on master.securerepo.com, it should do so using the username 'dev' and consulting gpg-agent for the password.
Let's Do This
Before we tie all this together, let's look at how this supposed to work. Suppose you enter the command:
$ svn checkout https://master.securerepo.com/projects/borken/trunk
Subversion will look for the configuration file that correspond to the realm string <https://master.securerepo.com:443 SVN>. In our example above, it will find this in the file named 3f08d77847ea42f0b7b1ccd66fd14138. It will then use this filename to ask gpg-agent for the credentials matching the keygrip 3f08d77847ea42f0b7b1ccd66fd14138. Our goal, therefore, is to to preset our passphrase for this hex value using the password found in 1password.
Here's one solution to accomplish this:
# sign in to 1password $ eval $(op signin allmysecrets) # store the svn repo password in a shell variable $ svn_password=$(op get item 'master svn account' | \ jq -r '.details.fields[] | select(.designation=="password").value') # store the password in gpg-agent with the correct hex key $ /usr/lib/gnupg/gpg-preset-passphrase -c -P "$svn_password" 3f08d77847ea42f0b7b1ccd66fd14138 # And we're done! svn should find the credentials in gpg-agent and # not bother asking us $ svn checkout https://master.securerepo.com/projects/borken/trunk src $ cd src $ svn switch ^/branches/feature-x
I have a shell script that contains a mapping of svn domains to 1password uuid's. This let's me run a single command to authenticate all svn domains in one go. Every time I run it I feel a bit of joy; my passwords are securely stored in 1password and they are seamlessly available to svn.
It was a long journey to get this all sorted, but it was so worth it!
No comments:
Post a Comment