--- a/.hgignore Sat Dec 04 20:22:01 2010 +0000
+++ b/.hgignore Fri Dec 17 21:00:06 2010 +0000
@@ -1,4 +1,5 @@
^build/
+^dev/chroot-test/build/
^build-stamp$
^configure-stamp$
^debian/files$
@@ -11,4 +12,3 @@
*.pyc
*.orig
*.rej
-
--- a/CREDITS Sat Dec 04 20:22:01 2010 +0000
+++ b/CREDITS Fri Dec 17 21:00:06 2010 +0000
@@ -17,6 +17,8 @@
Martin Bagge <brother@bsnet.se>
Vincenzo Campanella <vinz65@gmail.com>
Ji ZhengYu <zhengyuji@gmail.com>
+Waldemar Augustyn <waldemar@astyn.com>
+Steven King <kingrst@gmail.com>
This credits file may be incomplete - please remind me about people I
should add!
--- a/NEWS Sat Dec 04 20:22:01 2010 +0000
+++ b/NEWS Fri Dec 17 21:00:06 2010 +0000
@@ -1,3 +1,18 @@
+======================
+mercurial-server 1.1
+======================
+
+* Allow subrepo creation
+* New log filename
+* Changed logging format to use JSON/YAML
+* Add the source IP address and other info in the SSH_CONNECTION environment variable
+* Lock log file
+* Make sure authorized_keys file is mode 600
+* Add dev/chroot-test testing code
+* Extend documentation
+
+Upgrading: note the changes to the log file format listed above.
+
======================
mercurial-server 1.0.1
======================
--- a/README Sat Dec 04 20:22:01 2010 +0000
+++ b/README Fri Dec 17 21:00:06 2010 +0000
@@ -6,7 +6,7 @@
http://www.lshift.net/mercurial-server.html
-Copyright (C) 2008-2009 LShift Ltd.
+Copyright (C) 2008-2010 LShift Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,5 +40,5 @@
See doc/manual.docbook for the rest of the documentation.
-Paul Crowley, paul@lshift.net, 2009
+Paul Crowley, paul@lshift.net, 2010
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/chroot-test/action/go Fri Dec 17 21:00:06 2010 +0000
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+set -e
+
+cd action
+
+ls -l
+
+. ./testing_system
+
+#exec > results 2>&1
+
+#aptitude --allow-untrusted --quiet --without-recommends --assume-yes install mercurial
+#hg --version
+
+aptitude --allow-untrusted --quiet --without-recommends --assume-yes install \
+ make mercurial xsltproc docbook-xsl openssh-server \
+ python python-support adduser
+
+#aptitude --allow-untrusted --quiet --without-recommends --assume-yes install \
+# debconf python python-support adduser mercurial openssh-server
+
+
+perl -i -pe 's/^Port 22$/Port 2222/' /etc/ssh/sshd_config
+/etc/init.d/ssh start
+ssh-keyscan -p 2222 localhost > /etc/ssh/ssh_known_hosts
+
+#dpkg -i mercurial-server_1.0-1_all.deb
+cd mercurial-server
+make setup-adduser
+
+for user in test1 test2 ; do
+ adduser --gecos $user --disabled-password $user
+ su -l -c 'mkdir .ssh' $user
+ su -l -c 'ssh-keygen -N "" -f .ssh/id_rsa -t rsa' $user
+done
+cp /home/test1/.ssh/id_rsa.pub /etc/mercurial-server/keys/root/test1
+su -l -c /usr/local/share/mercurial-server/refresh-auth hg
+su -l -c /action/test1 test1
+su -l -c /action/test2 test2
+
+/etc/init.d/ssh stop
+
+#touch results
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/chroot-test/action/test1 Fri Dec 17 21:00:06 2010 +0000
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+set -e
+
+cd
+
+cat > .ssh/config <<__END__
+Host chroothg
+Hostname localhost
+User hg
+Port 2222
+__END__
+
+echo "Cloning hgadmin..."
+hg clone ssh://chroothg/hgadmin
+echo "Updating hgadmin..."
+cd hgadmin
+
+cat > access.conf <<__END__
+read user=restricted/** file=denied/**
+write user=restricted/**
+__END__
+mkdir -p keys/restricted
+cp /home/test2/.ssh/id_rsa.pub keys/restricted/test2
+
+hg add keys/restricted/test2 access.conf
+hg commit -u test1 -m "Added user test2"
+echo "Push"
+hg push
+cd ..
+hg init realrepo
+cd realrepo
+mkdir denied
+echo "This is a file" > content
+echo "This is a file not everyone can write to" > denied/cantwrite
+hg init nested
+echo "This is a file in a nested repo" > nested/content
+hg add -R nested nested/content
+hg commit -u test1 -R nested -m "Add files to the subrepo"
+echo "nested = nested" > .hgsub
+hg add content denied/cantwrite .hgsub
+hg commit -u test1 -m "Add files to the repo"
+echo "Pushing changes"
+hg clone . ssh://chroothg/real/project
+hg clone nested ssh://chroothg/real/project/nested
+echo "Done for user test1"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/chroot-test/action/test2 Fri Dec 17 21:00:06 2010 +0000
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+set -e
+
+cd
+
+cat > .ssh/config <<__END__
+Host chroothg
+Hostname localhost
+User hg
+Port 2222
+__END__
+
+echo "Pulling real project"
+hg clone ssh://chroothg/real/project
+cd project
+[ -e nested/content ]
+echo "and I'm adding something" >> content
+hg commit -u test2 -m "Added something to the file"
+echo "This push should succeed"
+hg push
+echo "And it did"
+echo "This should fail" >> denied/cantwrite
+hg commit -u test2 -m "WONTPUSH"
+# Fail only if this succeeds
+echo "About to do bad push"
+hg push && false
+echo "really checking now"
+hg outgoing --template '{desc}' | grep -q WONTPUSH
+echo "done"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/chroot-test/action/testing_system Fri Dec 17 21:00:06 2010 +0000
@@ -0,0 +1,5 @@
+
+if [ \! -e /please-trash-this-system ] ; then
+ echo "These tests will only run in a special test environment, sorry"
+ exit -1
+fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/chroot-test/policy-rc.d Fri Dec 17 21:00:06 2010 +0000
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# Refuse everything - nothing should start.
+return 101
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/chroot-test/run-test Fri Dec 17 21:00:06 2010 +0000
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# Must be root to run this
+
+set -e
+
+DEBVERSION=sid
+
+PRISTINE=/var/local/cache/pristine/$DEBVERSION
+
+if [ ! -e $PRISTINE ] ; then
+ echo "Missing:" $PRISTINE
+ echo "Debian pristine image not found, try running these commands as root:"
+ echo mkdir -p /var/local/cache/pristine
+ echo debootstrap $DEBVERSION $PRISTINE http://ftp.uk.debian.org/debian/
+ exit -1
+fi
+
+TOPDIR=`pwd`
+mkdir -p $TOPDIR/build/env
+BACKING=$TOPDIR/build/env/backing
+MOUNT=$TOPDIR/build/env/mount
+if [ -e $MOUNT ] ; then
+ echo "Removing old filesystem"
+ # FIXME: evil hack!
+ chroot $MOUNT /etc/init.d/ssh stop || true
+ umount $MOUNT/proc || true
+ umount $MOUNT || true
+ rm -rf $MOUNT
+fi
+if [ -e $BACKING ] ; then
+ echo "Copying deb files into cache"
+ cp $BACKING/var/cache/apt/archives/* build/aptcache/$DEBVERSION || true
+ echo "Deleting old filesystem backing store"
+ rm -rf $BACKING
+fi
+mkdir $BACKING $MOUNT
+
+touch $BACKING/please-trash-this-system
+mkdir -p $BACKING/etc
+echo "pristine" > $BACKING/etc/debian_chroot
+cp -v policy-rc.d $BACKING/etc/policy-rc.d
+
+mkdir -p $BACKING/var/cache/apt/archives
+echo "Copying deb files out of cache"
+cp build/aptcache/$DEBVERSION/* $BACKING/var/cache/apt/archives || true
+
+cp -av action $BACKING
+#hg -R ../mercurial-server archive -r default $BACKING/action/mercurial-server
+( cd $(hg root) && \
+ hg st -mac0n | cpio -p -0 -d $BACKING/action/mercurial-server )
+
+#cp ../mercurial-server/build/debian/mercurial-server_1.0-1_all.deb $BACKING/action
+
+unionfs-fuse -o cow -o allow_other,suid,dev $BACKING=RW:$PRISTINE=RO $MOUNT
+
+#mount --bind /dev "$MOUNT/dev"
+#mount --bind /dev/pts "$MOUNT/dev/pts"
+mount -t proc proc $MOUNT/proc
+
+chroot $MOUNT ./action/go
+
+
+#cat $BACKING/action/results
+
+#rm -rf build/env
+
--- a/doc/manual.docbook Sat Dec 04 20:22:01 2010 +0000
+++ b/doc/manual.docbook Fri Dec 17 21:00:06 2010 +0000
@@ -4,7 +4,7 @@
<info>
<title>Sharing Mercurial repositories with mercurial-server</title>
<author><firstname>Paul</firstname><surname>Crowley</surname></author>
- <copyright><year>2009</year><holder>Paul Crowley, LShift Ltd</holder></copyright>
+ <copyright><year>2008-2010</year><holder>Paul Crowley, LShift Ltd</holder></copyright>
</info>
<section>
<title>About mercurial-server</title>
@@ -420,6 +420,8 @@
</section>
</section>
<section>
+<title>In detail</title>
+<section>
<title>How mercurial-server works</title>
<para>
All of the repositories controlled by mercurial-server are owned by a
@@ -475,12 +477,31 @@
<para>
However, while the first paragraph holds no matter what bugs
mercurial-server contains, the second depends on the relevant code being
-correct; though the entire codebase is short, mercurial-server is a fairly
-new program and may harbour bugs. Backups are essential!
+correct; though the entire codebase is short, like all software mercurial-server may harbour bugs. Backups are essential!
+</para>
+</section>
+<section>
+<title>Logging</title>
+<para>
+Every successful access is logged in a file called
+<filename>~hg/repos/<replaceable>repository</replaceable>/.hg/mercurial-server.log</filename>. This file is in YAML format for easy parsing, but if you don't like YAML, simply treat each line as a JSON data structure prepended with <code>- </code>. The log records the time as a
+UTC ISO 8601 time, the operation ("push" or "pull"), the path to the key as
+used in the access rules, the SSH connection information (including the source IP address), and the hex changeset IDs.
</para>
</section>
<section>
-<title>Legalese</title>
+<title>Paths and configuration</title>
+<para>
+For security reasons, all mercurial-server code runs as the <systemitem
+class="username">hg</systemitem> user. The first thing this code reads when it starts is <filename>~hg/.mercurial-server</filename>; if this file is absent or corrupt the code won't run. This file specifies all of the file paths that mercurial-server uses. In particular, it specifies that mercurial-server always uses <code>HGRCPATH = /etc/mercurial-server/remote-hgrc.d</code> for remote operations, overriding any system <code>HGRCPATH</code>.
+</para>
+<para>
+By creating such a file with suitable entries, you can run mercurial-server as a user other than <systemitem
+class="username">hg</systemitem>, or install it without root privileges; however I strongly recommend that if you need to do this, you use a user account that is used for no other purpose, and take the time to thoroughly understand how mercurial-server works before you attempt it.
+</para>
+</section>
+<section>
+<title>License</title>
<para>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -506,8 +527,9 @@
it.
</para>
<para>
-Paul Crowley, <email>paul@lshift.net</email>, 2009
+Paul Crowley, <email>paul@lshift.net</email>, 2010
</para>
</section>
+</section>
</article>
--- a/setup.py Sat Dec 04 20:22:01 2010 +0000
+++ b/setup.py Fri Dec 17 21:00:06 2010 +0000
@@ -7,7 +7,7 @@
name="mercurial-server",
description="Centralized Mercurial repository manager",
url="http://www.lshift.net/mercurial-server.html",
- version="1.0.1", # FIXME: infer this
+ version="1.1", # FIXME: infer this
package_dir = {'': 'src'},
packages = ["mercurialserver"],
requires = ["mercurial"], # FIXME: what version?
--- a/src/hg-ssh Sat Dec 04 20:22:01 2010 +0000
+++ b/src/hg-ssh Fri Dec 17 21:00:06 2010 +0000
@@ -34,17 +34,8 @@
if head:
checkDots(head)
-def checkParents(path):
- path = os.path.dirname(path)
- if path == "":
- return
- if os.path.exists(path + "/.hg"):
- fail("Cannot create repo under existing repo")
- checkParents(path)
-
def getrepo(op, repo):
# First canonicalise, then check the string, then the rules
- # and finally the filesystem.
repo = repo.strip().rstrip("/")
if len(repo) == 0:
fail("path to repository seems to be empty")
@@ -54,7 +45,6 @@
ruleset.rules.set(repo=repo)
if not ruleset.rules.allow(op, branch=None, file=None):
fail("access denied")
- checkParents(repo)
return repo
config.initExe()
--- a/src/mercurialserver/refreshauth.py Sat Dec 04 20:22:01 2010 +0000
+++ b/src/mercurialserver/refreshauth.py Fri Dec 17 21:00:06 2010 +0000
@@ -4,7 +4,7 @@
import re
import base64
-import os
+import os, stat
import os.path
import subprocess
from mercurialserver import config
@@ -55,6 +55,7 @@
if len(l):
akeys.write('%s"%s %s" %s\n' % (prefix, wrappercommand, keyname, l))
akeys.close()
+ os.chmod(akeyfile + "_new", stat.S_IRUSR)
os.rename(akeyfile + "_new", akeyfile)
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
--- a/src/mercurialserver/servelog.py Sat Dec 04 20:22:01 2010 +0000
+++ b/src/mercurialserver/servelog.py Fri Dec 17 21:00:06 2010 +0000
@@ -8,6 +8,8 @@
import os
import time
+import fcntl
+import json
from mercurialserver import ruleset, changes
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
@@ -18,13 +20,18 @@
else:
raise mercurial.util.Abort(_('servelog installed as wrong hook type,'
' must be changegroup or outgoing but is %s') % hooktype)
- t = time.strftime("%Y-%m-%d_%H:%M:%S", time.gmtime())
- user = ruleset.rules.get('user')
- # FIXME: lock it
- log = open(repo.join("serve-log"), "a+")
+ log = open(repo.join("mercurial-server.log"), "a+")
try:
- for ctx in changes.changes(repo, node):
- log.write("%s %s key=%s changeset=%s\n" %
- (t, op, user, mercurial.node.hex(ctx.node())))
+ fcntl.flock(log.fileno(), fcntl.LOCK_EX)
+ log.seek(0, os.SEEK_END)
+ # YAML log file format
+ log.write("- {0}\n".format(json.dumps(dict(
+ timestamp=time.strftime("%Y-%m-%d_%H:%M:%S Z", time.gmtime()),
+ op=op,
+ key=ruleset.rules.get('user'),
+ ssh_connection=os.environ['SSH_CONNECTION'],
+ nodes=[mercurial.node.hex(ctx.node())
+ for ctx in changes.changes(repo, node)],
+ ))))
finally:
log.close()