Differences between revisions 5 and 36 (spanning 31 versions)
Revision 5 as of 2006-01-26 17:44:42
Size: 6592
Comment: point link to raw file version of contrib/hg-ssh
Revision 36 as of 2009-03-17 13:35:52
Size: 1935
Editor: ciphergoth
Comment: HgLogin now has its own page
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
is setting up a central repository every user pushes his changes to and pulls is setting up a central [:Repository:repository] every user pushes his changes to and pulls
Line 4: Line 4:
accessible via a shared ssh account. accessible via a shared ssh account without needing to give full shell access
to other people
.
Line 6: Line 7:
    '''Note:''' The following instructions describe the very personal setup we
    use on our system. I decided to add this page because the configuration
    described here a) works for mercurial out of the box and b) solves some
    problems from [http://www.kitenet.net/~joey/sshcvs/]: In particular, it
    allows distinguishing multiple committers and a (crude) form of permissions.
    It is most probably neither the best nor the most elegant way and I don't
    promise anything more than that it works for me.
    --- MarcSchaefer

    '''Note 2:''' A python script similar to the hg-login script described here is available in
    current tip in [http://www.selenic.com/hg/?f=-1;file=contrib/hg-ssh;style=raw contrib/hg-ssh].
    Allowed repositories are managed directly in the authorized_keys file, look at the start of the script for details.
    --- ThomasArendsenHein
[[TableOfContents]]
Line 22: Line 11:
When accessing a remote repository via mercurial's ssh repository type, ''hg'' When accessing a remote repository via Mercurial's ssh repository type, ''hg''
Line 26: Line 15:
$ ssh remote.server hg -R /path/to/repos serve --stdio $ ssh hg.example.com hg -R /path/to/repos serve --stdio
Line 31: Line 20:
can do all the sanity checks we want and then execs ''hg'' just like ssh would can do all the sanity checks we want and then calls ''hg'' just like ssh would
Line 33: Line 22:
his own entry in authorized_keys, which allows the script to distinguish his own entry in authorized_keys, which allows the scripts to distinguish
Line 36: Line 25:
== Setting up the shared SSH account ==
Line 38: Line 26:
The first step is creating a dedicated user on the server side -- let's call
it ''mercurial''. Nobody should be able to log into this account with a
password, so set the password field in the /etc/passwd to *. It needs a valid
shell though, since sshd always calls scripts through the shell. Then, copy
the ''hg-login'' script at the end of this page into the home directory
and create a directory ''repositories'', which will contain (wait for it)
the repositories (duh).
There are three alternative implementations of scripts which provide access only to
explicitly allowed repositories:
Line 46: Line 29:
Note that everybody with read/write permissions to the ''repository'' directory
can read/write to the repositories directly, so you might want to prevent
that.
=== mercurial-server ===
Line 50: Line 31:
== Allowing connections from a user ==   A set of tools available from [http://hg.opensource.lshift.net/mercurial-server/] which control access via a magic
  repository containing SSH keys and a rules file; changes to this repository change permissions automatically. For
  details see [http://hg.opensource.lshift.net/mercurial-server/file/tip/README].
Line 52: Line 35:
Every user needs his own public/private key (see the manpage of ''ssh-keygen''
for how to create one). Append it to ''~mercurial/.ssh/authorized_keys''
on the server side, prefixed with some options to grant access to mercurial only.
More precisely, every line has to look like this:
=== hg-ssh ===
Line 57: Line 37:
{{{
command="/home/mercurial/hg-login [user]",no-port-forwarding,no-X11-forwarding,no-pty ssh-[type] [key]
}}}
  A python script available in
  [http://www.selenic.com/repo/hg-stable/raw-file/tip/contrib/hg-ssh contrib/hg-ssh].
  Allowed repositories are managed directly in the authorized_keys file.
Line 61: Line 41:
Here ''[user]'' is an identifier which will later be used for granting
access to a repository, ''[type]'' is dsa or rsa depending on the key type
and ''[key]'' is the key itself, followed by an optional comment.
  Look at the start of the script for usage instructions.
Line 65: Line 43:
On every connect, the user must be able to present the corresponding
private key, for example by adding it to his ssh-agent.
Line 68: Line 44:
== Creating repositories and setting permissions == === hg-login ===
Line 70: Line 46:
Creating a shared repository is simple: Just initialise it in ''repositories''
like every other repository. However, nobody will be able to access it unless
you grant them permission. To allow a user to access the repository
''~mercurial/repositories/<repos>'', create a file
''~mercurial/repositories/<repos>.allow''
which contains his username (the one from ''authorized_keys'') alone on a line.
  HgLogin is a system by MarcSchaefer for achieving the same end.
Line 77: Line 48:
Note that it is not possible to only grant read rights -- it's full access
or nothing.
See also AclExtension, HgWebDirStepByStep.
Line 80: Line 50:
== The hg-login script ==

The following is a (Perl) script (sorry ;) ) to mediate the access to the
shared repositories. It first of all checks the supplied username and the
command that is to be executed for sanity (usernames must be alphanumeric,
starting with a letter), then normalises and checks the repository path
(creating subdirectories in ''repositories'' is allowed, but file names
must match ^[a-zA-Z0-9][a-zA-Z0-9-:+.]$). Only if these checks pass
and the desired repository exists and allows access by the user, the
server process is started.

{{{
#!/usr/bin/perl -w -T
use strict;

$ENV{PATH} = '/usr/local/bin:/usr/bin:/bin';

my $hg = '/usr/local/bin/hg';
my $repositories = '/home/mercurial/repositories';

# The following character classes describe the allowed user-
# and repository names. Note that we forbid all path constituents
# which begin with a dot -- look ma, no directory traversal.

my $r_user = qr#[a-zA-Z][a-zA-Z0-9]*#;
my $r_file = qr#[a-zA-Z0-9][a-zA-Z0-9-:+.]*#;

# The username is given as the first argument (from command=
# in authorized_keys), sshd is kind enough to pass the requested
# command as an environment variable.

my $user_in = $ARGV[0];
my $cmd_in = $ENV{SSH_ORIGINAL_COMMAND} || '';

# First, basic sanity checking on the username. The assignment
# is necessary to convince Perl that the username is no longer
# tainted.

defined $user_in
    or die "No username given.\n";
my ($user) = $user_in =~ /^($r_user)$/
    or die "Invalid username `$user_in'.}n";

# The command passed by hg has a very specific structure: Check that.

my ($repos) = $cmd_in =~ m#^hg -R (\S+) serve --stdio$#
    or die "Invalid command `$cmd_in' requested.\n";

# Now for the repository path: We assume that it consists of $r_files
# separated by slashes. Leading and trailing ones are ignored.

s#^/+##, s#/+$##, s#/+#/#g for $repos;

my $path = '';
foreach my $file_in (split m#/#, $repos) {
    my ($file) = $file_in =~ /^($r_file)$/
        or die "Invalid repository path `$repos'";
    $path .= "/$file";
}

# Only the toplevel-directory of every mercurial repository contains
# a subdir `.hg'.

-d "$repositories/$path/.hg" or die "No such repository `$path'.\n";

# Now for permissions ...

open my $perms, '<', "$repositories/$path.allow"
    or die "No such repositoriy `$path'.\n";

chomp( my @allowed_in = <$perms> );

close $perms;

my $allowed = '';
$user eq $_ and $allowed = 1 for @allowed_in;
$allowed or die "No such repository `$path'.\n";

# Ok, everything is in order: go for it.

exec $hg, '-R', "$repositories/$path", 'serve', '--stdio';
die "Unable to exec `hg' on repository `$path' ($!)\n";
}}}
----
CategoryWeb CategoryHowTo

As described on MultipleCommitters, one way of collaboration (the CVS-like model) is setting up a central [:Repository:repository] every user pushes his changes to and pulls the others' changes from. This page describes how to create such repositories accessible via a shared ssh account without needing to give full shell access to other people.

TableOfContents

How this works

When accessing a remote repository via Mercurial's ssh repository type, hg basically does a

$ ssh hg.example.com hg -R /path/to/repos serve --stdio

and relies on ssh for authentication and tunneling. When using public key authentication, ssh allows limiting the user to one specific command, which can do all the sanity checks we want and then calls hg just like ssh would in the example above. Note that every user gets his own private key and his own entry in authorized_keys, which allows the scripts to distinguish between different users and thus enforce e.g. access permissions.

There are three alternative implementations of scripts which provide access only to explicitly allowed repositories:

mercurial-server

hg-ssh

hg-login

See also AclExtension, HgWebDirStepByStep.


CategoryWeb CategoryHowTo

SharedSSH (last edited 2021-03-19 07:37:31 by RobinMunn)