SVN and permissions
Quite often I hear people complaining “BDB sucks, it breaks here twice a day! I will use FSFS now. BDBjust sucks”. This seems true at first. But in many cases, it’s an administration error.
As the svn book and FAQ point out in many places, you need to take care of the permissions. The first important link with BDB and permissions might be the FAQ entry “Why do read-only operations still need repository write access?”. It explains why BDB requires write permission to the repository directory even for readonly operations. At this point, FSFS seems distinctly better than BDB, because FSFS only requires read permissions for readonly operations.
For our checklist we keep in mind that a user needs write permissions for reading the repos.
Now we have to sort out the question “Who needs access to our repos?” I will discuss some common setups here.
The basic setup
$ mkdir -p /srv/svn/repositories $ svnadmin create /srv/svn/repositories/project01 $ cd /srv/svn/repositories/project01 $ find . -ls 31320846 0 drwxr-xr-x 7 root root 90 Apr 9 00:14 . 2272808 0 drwxr-xr-x 2 root root 6 Apr 9 00:14 ./dav 7178361 0 drwxr-xr-x 2 root root 39 Apr 9 00:14 ./locks 7178364 4 -rw-r--r-- 1 root root 460 Apr 9 00:14 ./locks/db.lock 7178366 4 -rw-r--r-- 1 root root 295 Apr 9 00:14 ./locks/db-logs.lock 8854953 4 drwxr-xr-x 2 root root 4096 Apr 9 00:14 ./hooks 11228932 4 -rw-r--r-- 1 root root 2137 Apr 9 00:14 ./hooks/start-commit.tmpl 11228937 4 -rw-r--r-- 1 root root 2944 Apr 9 00:14 ./hooks/pre-commit.tmpl 11228939 4 -rw-r--r-- 1 root root 2764 Apr 9 00:14 ./hooks/pre-revprop-change.tmpl 11228940 4 -rw-r--r-- 1 root root 2016 Apr 9 00:14 ./hooks/pre-lock.tmpl 11228943 4 -rw-r--r-- 1 root root 1998 Apr 9 00:14 ./hooks/pre-unlock.tmpl 11228944 4 -rw-r--r-- 1 root root 2015 Apr 9 00:14 ./hooks/post-commit.tmpl 11228946 4 -rw-r--r-- 1 root root 1637 Apr 9 00:14 ./hooks/post-lock.tmpl 11231620 4 -rw-r--r-- 1 root root 1564 Apr 9 00:14 ./hooks/post-unlock.tmpl 11237184 4 -rw-r--r-- 1 root root 2255 Apr 9 00:14 ./hooks/post-revprop-change.tmpl 13642827 0 drwxr-xr-x 2 root root 39 Apr 9 00:14 ./conf 13642830 4 -rw-r--r-- 1 root root 1078 Apr 9 00:14 ./conf/svnserve.conf 13642831 4 -rw-r--r-- 1 root root 311 Apr 9 00:14 ./conf/passwd 31320848 4 -rw-r--r-- 1 root root 379 Apr 9 00:14 ./README.txt 31320850 4 -r--r--r-- 1 root root 2 Apr 9 00:14 ./format <strong>18259246 0 <u>drwxr-sr-x</u> 5 root root 120 Apr 9 00:14 ./db</strong> 18272163 4 -rw-r--r-- 1 root root 5 Apr 9 00:14 ./db/fs-type 23169557 0 drwxr-sr-x 2 root root 14 Apr 9 00:14 ./db/revs 23169558 4 -rw-r--r-- 1 root root 115 Apr 9 00:14 ./db/revs/0 27193648 0 drwxr-sr-x 2 root root 14 Apr 9 00:14 ./db/revprops 27193649 4 -rw-r--r-- 1 root root 50 Apr 9 00:14 ./db/revprops/0 31320852 0 drwxr-sr-x 2 root root 6 Apr 9 00:14 ./db/transactions 18272185 4 -rw-r--r-- 1 root root 6 Apr 9 00:14 ./db/current 18272212 0 -rw-r--r-- 1 root root 0 Apr 9 00:14 ./db/write-lock 18273153 4 -rw-r--r-- 1 root root 37 Apr 9 00:14 ./db/uuid 18273164 4 -r--r--r-- 1 root root 2 Apr 9 00:14 ./db/format
In the output of “find” we can already see one important point: newer svn releases set the ”<a href=”http://www.gnu.org/software/coreutils/manual/html_chapter/coreutils_26.html#SEC159”>set group id” flag on the db directory. This flag makes sure that all files created in the “db” directory are owned by the same group as the “db” directory.
Basic Concept
Note that the setgid bit doesn’t suffice to ensure correct permissions: we can only enforce the groupownership, but not enforce the permissions the new file will get—the umask of the user influences those. A typical umask value is “022”; if you create a file with this umask it will get the following permissions:
$ umask 022 $ touch file $ ls -l file -rw-r--r-- 1 darix users 0 2005-04-09 00:41 file
As we see the group can only read the file. This is fine for FSFS, but for BDB we would need write permissions.
$ umask 007 $ touch file $ ls -l file -rw-rw---- 1 darix users 0 2005-04-09 00:44 file
Setting the umask to “007” gives the group write permissions and removes all permissions for others. a more liberal umask would be “002” which would allow the others at least read permissions. A paranoid user would set “077” which takes permissions away from all other users (note that this umask would wreak havoc with either flavor of subversion repository). The complete magic is explained in man umask.
FSFS takes care of this itself. If FSFS backend creates a new file it uses the permissions of the previous revision. This permission inheritance makes sure that the group can always write the files.
For the checklist we keep “The umask can be important!”
Apache2/Svnserve Only
These are for sure the easiest cases. You should run them as a non-root user!
I assume you run Apache2 as “apache:apache” and svnserve as “svn:svn”:
$ chown -R apache: /srv/svn/repositories/project01/db $ find . -ls 31320846 0 drwxr-xr-x 7 root root 90 Apr 9 00:14 . 2272808 0 drwxr-xr-x 2 root root 6 Apr 9 00:14 ./dav 7178361 0 drwxr-xr-x 2 root root 39 Apr 9 00:14 ./locks 7178364 4 -rw-r--r-- 1 root root 460 Apr 9 00:14 ./locks/db.lock 7178366 4 -rw-r--r-- 1 root root 295 Apr 9 00:14 ./locks/db-logs.lock 8854953 4 drwxr-xr-x 2 root root 4096 Apr 9 00:14 ./hooks 11228932 4 -rw-r--r-- 1 root root 2137 Apr 9 00:14 ./hooks/start-commit.tmpl 11228937 4 -rw-r--r-- 1 root root 2944 Apr 9 00:14 ./hooks/pre-commit.tmpl 11228939 4 -rw-r--r-- 1 root root 2764 Apr 9 00:14 ./hooks/pre-revprop-change.tmpl 11228940 4 -rw-r--r-- 1 root root 2016 Apr 9 00:14 ./hooks/pre-lock.tmpl 11228943 4 -rw-r--r-- 1 root root 1998 Apr 9 00:14 ./hooks/pre-unlock.tmpl 11228944 4 -rw-r--r-- 1 root root 2015 Apr 9 00:14 ./hooks/post-commit.tmpl 11228946 4 -rw-r--r-- 1 root root 1637 Apr 9 00:14 ./hooks/post-lock.tmpl 11231620 4 -rw-r--r-- 1 root root 1564 Apr 9 00:14 ./hooks/post-unlock.tmpl 11237184 4 -rw-r--r-- 1 root root 2255 Apr 9 00:14 ./hooks/post-revprop-change.tmpl 13642827 0 drwxr-xr-x 2 root root 39 Apr 9 00:14 ./conf 13642830 4 -rw-r--r-- 1 root root 1078 Apr 9 00:14 ./conf/svnserve.conf 13642831 4 -rw-r--r-- 1 root root 311 Apr 9 00:14 ./conf/passwd 31320848 4 -rw-r--r-- 1 root root 379 Apr 9 00:14 ./README.txt 31320850 4 -r--r--r-- 1 root root 2 Apr 9 00:14 ./format 18259246 0 drwxr-sr-x 5 apache apache 120 Apr 9 00:14 ./db 18272163 4 -rw-r--r-- 1 apache apache 5 Apr 9 00:14 ./db/fs-type 23169557 0 drwxr-sr-x 2 apache apache 14 Apr 9 00:14 ./db/revs 23169558 4 -rw-r--r-- 1 apache apache 115 Apr 9 00:14 ./db/revs/0 27193648 0 drwxr-sr-x 2 apache apache 14 Apr 9 00:14 ./db/revprops 27193649 4 -rw-r--r-- 1 apache apache 50 Apr 9 00:14 ./db/revprops/0 31320852 0 drwxr-sr-x 2 apache apache 6 Apr 9 00:14 ./db/transactions 18272185 4 -rw-r--r-- 1 apache apache 6 Apr 9 00:14 ./db/current 18272212 0 -rw-r--r-- 1 apache apache 0 Apr 9 00:14 ./db/write-lock 18273153 4 -rw-r--r-- 1 apache apache 37 Apr 9 00:14 ./db/uuid 18273164 4 -r--r--r-- 1 apache apache 2 Apr 9 00:14 ./db/format
For svnserve, of course, you’d instead do chown -R svn: /srv/svn/repositories/project01/db.
You might argue that one should further restrict the permissions, so that others can neither read nor write the “db” directory. You’d be right. My paranoid setup looks like this:
$ chmod -R o= /srv/svn/repositories/project01 $ chgrp -R apache /srv/svn/repositories/project01 $ find . -ls 31320846 0 drwxr-x--- 7 root apache 90 Apr 9 00:14 . 2272808 0 drwxr-x--- 2 root apache 6 Apr 9 00:14 ./dav 7178361 0 drwxr-x--- 2 root apache 39 Apr 9 00:14 ./locks 7178364 4 -rw-r----- 1 root apache 460 Apr 9 00:14 ./locks/db.lock 7178366 4 -rw-r----- 1 root apache 295 Apr 9 00:14 ./locks/db-logs.lock 8854953 4 drwxr-x--- 2 root apache 4096 Apr 9 00:14 ./hooks 11228932 4 -rw-r----- 1 root apache 2137 Apr 9 00:14 ./hooks/start-commit.tmpl 11228937 4 -rw-r----- 1 root apache 2944 Apr 9 00:14 ./hooks/pre-commit.tmpl 11228939 4 -rw-r----- 1 root apache 2764 Apr 9 00:14 ./hooks/pre-revprop-change.tmpl 11228940 4 -rw-r----- 1 root apache 2016 Apr 9 00:14 ./hooks/pre-lock.tmpl 11228943 4 -rw-r----- 1 root apache 1998 Apr 9 00:14 ./hooks/pre-unlock.tmpl 11228944 4 -rw-r----- 1 root apache 2015 Apr 9 00:14 ./hooks/post-commit.tmpl 11228946 4 -rw-r----- 1 root apache 1637 Apr 9 00:14 ./hooks/post-lock.tmpl 11231620 4 -rw-r----- 1 root apache 1564 Apr 9 00:14 ./hooks/post-unlock.tmpl 11237184 4 -rw-r----- 1 root apache 2255 Apr 9 00:14 ./hooks/post-revprop-change.tmpl 13642827 0 drwxr-x--- 2 root apache 39 Apr 9 00:14 ./conf 13642830 4 -rw-r----- 1 root apache 1078 Apr 9 00:14 ./conf/svnserve.conf 13642831 4 -rw-r----- 1 root apache 311 Apr 9 00:14 ./conf/passwd 31320848 4 -rw-r----- 1 root apache 379 Apr 9 00:14 ./README.txt 31320850 4 -r--r----- 1 root apache 2 Apr 9 00:14 ./format 18259246 0 drwxr-s--- 5 apache apache 120 Apr 9 00:14 ./db 18272163 4 -rw-r----- 1 apache apache 5 Apr 9 00:14 ./db/fs-type 23169557 0 drwxr-s--- 2 apache apache 14 Apr 9 00:14 ./db/revs 23169558 4 -rw-r----- 1 apache apache 115 Apr 9 00:14 ./db/revs/0 27193648 0 drwxr-s--- 2 apache apache 14 Apr 9 00:14 ./db/revprops 27193649 4 -rw-r----- 1 apache apache 50 Apr 9 00:14 ./db/revprops/0 31320852 0 drwxr-s--- 2 apache apache 6 Apr 9 00:14 ./db/transactions 18272185 4 -rw-r----- 1 apache apache 6 Apr 9 00:14 ./db/current 18272212 0 -rw-r----- 1 apache apache 0 Apr 9 00:14 ./db/write-lock 18273153 4 -rw-r----- 1 apache apache 37 Apr 9 00:14 ./db/uuid 18273164 4 -r--r----- 1 apache apache 2 Apr 9 00:14 ./db/format
This setup will allow apache to read all files in the repository directory but only write in the “db” directory. Other users than root or apache don’t have any access.
Allowing Svnserve and Apache2 to access the same repository
Now we get to the interesting problems: two daemons accessing the same repository. We have two possibile solutions:
- running both daemons as apache
- both daemons in a common group
running both daemons as apache
This is the easier of the two solutions. The permissions from above stay the same as above. We just need to start the svnserve as apache. On distributions with startproc it will look like:
$ startproc -u apache -g apache -e /usr/bin/svnserve -d -r /srv/svn/repositories
On systems with start-stop-daemon:
$ start-stop-daemon --start --chuid apache --group apache --exec /usr/bin/svnserve -- -d -r /srv/svn/repositories
For all other systems please refer to the manual of your os/distributon.
both daemons in a common group
For this approach we use point #2 from our checklist. First we need to decide which group we use. We can add the svn user to the group apache, or conversely we can add the apache user to the svn group. The latter has fewer possible security implications, so i will use this for my example:
# suse $ /usr/sbin/groupmod --add-user apache svn # freebsd $ pw groupmod -n svn -m apache
We can set our new permissions now. Based on the paranoid example from above
$ chgrp -R svn /srv/svn/repositories/project01
Now we have to make sure both daemons run with a proper umask. The umask is usually inherited from the startup environment. Usually this can cause trouble, as root usually has “022” as its umask. If you want to make sure that the umask of the daemon is always correct, add “umask 007” in your startscript. Et voila—svnserve and apache can both access the repos just fine.
At this moment we don’t care anymore if your apache is running mod_dav_svn or viewcvs or any other repository viewer. They all work just fine and don’t wedge the repository. (At least not for permissions reasons.)
Adding file:// and svn+ssh:// to the mix
svn+ssh:// and file:// both access the repository the same way. In general we have the same problems as in the last block. We need to make sure that all accessing users have a common group and a proper umask. The easiest solution for file and svn+ssh are wrapper scripts.
file:// – access
Our wrapper script for file:// access:
#!/bin/sh # wrapper script for svn umask 007 exec /usr/bin/svn "$@"
You have to put this script in the path before your actual svn binary (”/usr/bin/svn”), to ensure that your users run your script. First we need to check the $PATH variable.
$ env | grep -e "^PATH=" PATH=/home/darix/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/games:/opt/gnome/bin:/opt/kde3/bin
On my box, ”/usr/local/bin” is in the path before ”/usr/bin/”. I save the wrapper script as ”/usr/local/bin/svn”. Any invocations of “svn” will then invoke ”/usr/local/bin/svn”. You should look at gui clients/scripts which invoke svn: sometimes they use a full path, hence bypassing our script. You’d need to adjust them, too. (This doesn’t refer to scripts like websvn—it is run in the webserver context and should have the proper umask.)
svn+ssh://
The solution for svn+ssh:// follows the same path. We have a small wrapper script again:
#!/bin/sh # wrapper script for svnserve umask 007 exec /usr/bin/svnserve "$@"
To find the value of $PATH from a ssh session we do:
$ ssh localhost env | grep -e "^PATH=" PATH=/home/darix/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/games:/opt/gnome/bin:/opt/kde3/bin
Again, ”/usr/local/bin” seems to be a nice place for the wrapper script. We save the script as ”/usr/local/bin/svnserve”.
A note for BSD users: ”/usr/local/” is the default prefix for svn on a BSD, but put your wrapper scripts into ”/usr/bin” instead, because (at least, on my BSD box) ”/usr/bin” is in the path before ”/usr/local/bin”.
Conclusion
As shown in this short howto it is quite easy to have a proper setup with BDB without any permissions problems. So next time before you blame BDB … check your setup. From my day- by-day experience on#svn I can say 99% of the BDB wedges are permissions problems.