* [RFC] Secure central repositories by UNIX socket authentication
@ 2008-01-27 10:39 Shawn O. Pearce
2008-01-27 14:04 ` Johannes Schindelin
2008-01-27 22:56 ` Junio C Hamano
0 siblings, 2 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-27 10:39 UTC (permalink / raw)
To: git
This isn't anywhere near ready for application, but I'm floating
it out there to see what people think. Its a cool new feature that
will certainly *not* be in 1.5.4. :-)
In a central repository configuration users may not have access
to write new objects into a Git repository, or to edit the refs,
especially if the repository is being protected by an update hook
(e.g. contrib/hooks/updated-paranoid).
This change allows any repository owner to setup a git-daemon
that other users on the same host can connect through to perform
upload-pack or receive-pack. It also allows the update hook to
authorize the user via $GIT_REMOTE_USER. For example:
## Owner (not jdoe)
##
cat >foo.git/hooks/update <<'EOF'
#!/bin/sh
test -z "$GIT_REMOTE_USER" || exit
case "$GIT_REMOTE_USER" in
jdoe) exit 0;;
spearce) exit 0;;
*) exit 1
esac
EOF
chmod u+x foo.git/hooks/update
chmod 700 foo.git
git daemon \
--export-all \
--enable=receive-pack \
--base=`pwd` \
--listen=/tmp/shawn-git
## Other User
##
git push jdoe@server:/tmp/shawn-git/foo.git master
allows user jdoe to push into foo.git, even though it is restricted.
This works by:
*) git-push executes
"ssh jdoe@server git-receive-pack /tmp/shawn-git/foo.git"
*) git-receive-pack realizes its actually a socket, connects
to /tmp/shawn-git and asks for service "git-receive-pack"
on directory "/foo.git"
*) git-daemon authenticates the user via the kernel's secure
peer id call and sets GIT_REMOTE_USER with their username.
*) git-daemon starts another git-receive-pack on foo.git, using
its normal access control and path resolving rules.
*) The first git-receive-pack acts as a shuttle to copy
data between the two peers.
*) The second git-receive-pack runs the update hook, which has
GIT_REMOTE_USER defined from git-daemon.
There's a few small problems with this, which is partly why this
isn't ready for application yet:
1) Error messages from the pre-receive or update hooks don't make it
back to the client as send-pack and receive-pack don't implement
the sideband protocol. This is harder than it sounds as the
pkt-line protocol they currently use for status can't be easily
layered into a sideband protocol.
2) There is currently no "authorization" hook for upload-pack (aka
the fetch server) like we have for receive-pack (aka the push
server) so we cannot make use of $GIT_REMOTE_USER to perform
user authorization. Currently Git relies on UNIX filesystem
access, but this daemon approach bypasses those. There was a
conversation on #git tonight about adding a hook that filters
the list of allowed refs during fetch, for exactly this reason.
3) The system call to get the peer UID via the kernel is not POSIX
so its implemented differently on basically every OS we support,
if it is even implemented there. The patch below should provide
support for Solaris 8/9/10, FreeBSD, Darwin/Mac OS X, and Linux.
But is thus far only tested on Mac OS 10.4.
---
Documentation/git-daemon.txt | 11 ++-
Makefile | 14 ++-
builtin-upload-archive.c | 3 +
daemon-redirect.c | 282 ++++++++++++++++++++++++++++++++++++++++++
daemon-redirect.h | 12 ++
daemon.c | 132 +++++++++++++++-----
git-compat-util.h | 1 +
receive-pack.c | 3 +
upload-pack.c | 3 +
9 files changed, 429 insertions(+), 32 deletions(-)
create mode 100644 daemon-redirect.c
create mode 100644 daemon-redirect.h
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index fd83bc7..1f9d9a1 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -15,7 +15,7 @@ SYNOPSIS
[--reuseaddr] [--detach] [--pid-file=file]
[--enable=service] [--disable=service]
[--allow-override=service] [--forbid-override=service]
- [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]]
+ [--inetd | [--listen=host_or_ipaddr_or_path] [--port=n] [--user=user [--group=group]]
[directory...]
DESCRIPTION
@@ -79,12 +79,19 @@ OPTIONS
Have the server run as an inetd service. Implies --syslog.
Incompatible with --port, --listen, --user and --group options.
---listen=host_or_ipaddr::
+--listen=host_or_ipaddr_or_path::
Listen on an a specific IP address or hostname. IP addresses can
be either an IPv4 address or an IPV6 address if supported. If IPv6
is not supported, then --listen=hostname is also not supported and
--listen must be given an IPv4 address.
Incompatible with '--inetd' option.
++
+If the address is an absolute path in the filesystem then git-daemon
+listens on a UNIX domain socket at the given path. This can be used
+to support authenticated redirection from linkgit:git-upload-pack[1]
+or linkgit:git-receive-pack[1]. Child processes (including hooks)
+can obtain the authenticated username from the `GIT_REMOTE_USER`
+environment variable.
--port=n::
Listen on an alternative port. Incompatible with '--inetd' option.
diff --git a/Makefile b/Makefile
index a0c0998..6f8f3ad 100644
--- a/Makefile
+++ b/Makefile
@@ -295,7 +295,8 @@ LIB_H = \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
- mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
+ mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h \
+ daemon-redirect.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -309,6 +310,7 @@ LIB_OBJS = \
lockfile.o \
patch-ids.o \
object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
+ daemon-redirect.o \
sideband.o reachable.o reflog-walk.o \
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
@@ -417,6 +419,7 @@ ifeq ($(uname_S),Darwin)
endif
NO_STRLCPY = YesPlease
NO_MEMMEM = YesPlease
+ HAVE_GETPEEREID = YesPlease
endif
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
@@ -426,6 +429,7 @@ ifeq ($(uname_S),SunOS)
NO_MEMMEM = YesPlease
NO_HSTRERROR = YesPlease
NO_MKDTEMP = YesPlease
+ HAVE_STREAMS_CONNLD = YesPlease
ifeq ($(uname_R),5.8)
NEEDS_LIBICONV = YesPlease
NO_UNSETENV = YesPlease
@@ -463,6 +467,7 @@ endif
ifeq ($(uname_S),FreeBSD)
NEEDS_LIBICONV = YesPlease
NO_MEMMEM = YesPlease
+ HAVE_GETPEEREID = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
endif
@@ -470,6 +475,7 @@ ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NEEDS_LIBICONV = YesPlease
+ HAVE_GETPEEREID = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
endif
@@ -659,6 +665,12 @@ else
BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6
endif
endif
+ifdef HAVE_STREAMS_CONNLD
+ BASIC_CFLAGS += -DHAVE_STREAMS_CONNLD
+endif
+ifdef HAVE_GETPEEREID
+ BASIC_CFLAGS += -DHAVE_GETPEEREID
+endif
ifdef NO_INET_NTOP
LIB_OBJS += compat/inet_ntop.o
endif
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
index 48ae09e..5599e3b 100644
--- a/builtin-upload-archive.c
+++ b/builtin-upload-archive.c
@@ -5,6 +5,7 @@
#include "builtin.h"
#include "archive.h"
#include "pkt-line.h"
+#include "daemon-redirect.h"
#include "sideband.h"
static const char upload_archive_usage[] =
@@ -35,6 +36,8 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix)
strcpy(buf, argv[1]); /* enter-repo smudges its argument */
+ if (redirect_execute("upload-archive", buf))
+ return 0;
if (!enter_repo(buf, 0))
die("not a git archive");
diff --git a/daemon-redirect.c b/daemon-redirect.c
new file mode 100644
index 0000000..8474ddd
--- /dev/null
+++ b/daemon-redirect.c
@@ -0,0 +1,282 @@
+#include "daemon-redirect.h"
+#include "pkt-line.h"
+
+static int redirect_connect(const char *path);
+
+#ifdef HAVE_STREAMS_CONNLD
+
+int redirect_listen(const char *path)
+{
+ int fd_pair[2];
+
+ if (pipe(fd_pair) || ioctl(fd_pair[0], I_PUSH, "connld"))
+ die("redirect listen failed: %s", strerror(errno));
+ ioctl(fd_pair[0], I_CANPUT, 0);
+
+ unlink(path);
+ if (close(creat(path, 0666)) || fattach(fd_pair[0], path))
+ die("redirect listen failed on %s: %s", path, strerror(errno));
+ close(fd_pair[0]); /* can i really do that? */
+ return fd_pair[1];
+}
+
+int redirect_accept(int srv_fd, uid_t *cli_uid)
+{
+ struct strrecvfd rfd;
+
+ if (ioctl(srv_fd, I_RECVFD, &rfd))
+ return -1;
+ *cli_uid = rfd.uid;
+ return rfd.fd;
+}
+
+static int redirect_connect(const char *path)
+{
+ int cli_fd = open(path, O_RDWR);
+ if (cli_fd < 0)
+ die("redirect connect failed on %s: %s", path, strerror(errno));
+ return cli_fd;
+}
+
+#else /* ! HAVE_STREAMS_CONNLD */
+
+int redirect_listen(const char *path)
+{
+ int srv_fd;
+ struct sockaddr_un addr;
+
+ if (strlen(path) > sizeof(addr.sun_path))
+ die("redirect listen path too long");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+ unlink(path);
+
+ srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (srv_fd < 0)
+ die("redirect listen failed: %s", strerror(errno));
+ if (bind(srv_fd, (struct sockaddr*)&addr, sizeof(addr)))
+ die("redirect listen failed on %s: %s", path, strerror(errno));
+ if (listen(srv_fd, 10))
+ die("redirect listen failed: %s", strerror(errno));
+
+ return srv_fd;
+}
+
+static int peer_uid(int cli_fd, uid_t *cli_uid);
+
+#if defined(HAVE_GETPEEREID) /* OpenBSD / FreeBSD / Darwin */
+static int peer_uid(int cli_fd, uid_t *cli_uid)
+{
+ gid_t cli_gid;
+ return getpeereid(cli_fd, cli_uid, &cli_gid);
+}
+#elif defined(SO_PEERCRED) /* Linux */
+static int peer_uid(int cli_fd, uid_t *cli_uid)
+{
+ struct ucred creds;
+ int len = sizeof(creds);
+ if (getsockopt(cli_fd, SOL_SOCKET, SO_PEERCRED, &creds, &len))
+ return -1;
+ *cli_uid = creds.uid;
+}
+#else /* Not supported */
+static int peer_uid(int cli_fd, uid_t *cli_uid)
+{
+ error("UNIX peer authentication not compiled in");
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+int redirect_accept(int srv_fd, uid_t *cli_uid)
+{
+ int cli_fd;
+ struct sockaddr_un ss;
+ unsigned int sslen = sizeof(ss);
+
+ cli_fd = accept(srv_fd, (struct sockaddr*)&ss, &sslen);
+ if (cli_fd < 0)
+ return -1;
+
+ if (peer_uid(cli_fd, cli_uid)) {
+ error("redirect accept creds failed: %s", strerror(errno));
+ close(cli_fd);
+ errno = ECONNABORTED;
+ return -1;
+ }
+
+ return cli_fd;
+}
+
+static int redirect_connect(const char *path)
+{
+ int cli_fd;
+ struct sockaddr_un addr;
+
+ if (strlen(path) > sizeof(addr.sun_path))
+ die("redirect socket path too long");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+
+ cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (cli_fd < 0)
+ die("redirect connect failed: %s", strerror(errno));
+ if (connect(cli_fd, (struct sockaddr*)&addr, sizeof(addr)))
+ die("redirect connect failed on %s: %s", path, strerror(errno));
+ return cli_fd;
+}
+
+#endif
+
+struct fd_copy_buffer {
+ int live;
+ int in_fd;
+ int out_fd;
+ size_t avail;
+ size_t off;
+ char buf[4096];
+};
+
+static int buf_poll(struct pollfd *p, struct fd_copy_buffer *b)
+{
+ if (!b->live)
+ return 0;
+
+ if (b->avail) {
+ p->fd = b->out_fd;
+ p->events = POLLOUT;
+ } else {
+ p->fd = b->in_fd;
+ p->events = POLLIN;
+ }
+ return 1;
+}
+
+static int buf_copy(struct pollfd *p, struct fd_copy_buffer *b)
+{
+ if (!b->live)
+ return 0;
+
+ if (p->revents & POLLIN) {
+ b->avail = read(b->in_fd, b->buf, sizeof(b->buf));
+ b->off = 0;
+ if (!b->avail) {
+ b->live = 0;
+ close(b->in_fd);
+ shutdown(b->out_fd, 1);
+ } else if (b->avail < 0)
+ die("input failure: %s", strerror(errno));
+ }
+
+ if (p->revents & POLLOUT) {
+ ssize_t r = write(b->out_fd, b->buf + b->off, b->avail);
+ if (r <= 0)
+ die("output failure: %s", strerror(errno));
+ b->avail -= r;
+ b->off += r;
+ }
+
+ return 1;
+}
+
+static void execute(
+ const char *service,
+ const char *sock_name,
+ const char *repo_name)
+{
+ int cli_fd;
+ struct pollfd pfd[2];
+ struct fd_copy_buffer in_buf, out_buf;
+
+ cli_fd = redirect_connect(sock_name);
+ packet_write(cli_fd, "git-%s /%s", service, repo_name);
+
+ in_buf.live = 1;
+ in_buf.in_fd = 0;
+ in_buf.out_fd = cli_fd;
+ in_buf.avail = 0;
+
+ out_buf.live = 1;
+ out_buf.in_fd = cli_fd;
+ out_buf.out_fd = 1;
+ out_buf.avail = 0;
+
+ while (out_buf.live) {
+ int cnt = 0;
+
+ cnt += buf_poll(&pfd[cnt], &in_buf);
+ cnt += buf_poll(&pfd[cnt], &out_buf);
+
+ if (poll(pfd, cnt, -1) < 0) {
+ if (errno != EINTR) {
+ error("poll failed, resuming: %s", strerror(errno));
+ sleep(1);
+ }
+ continue;
+ }
+
+ cnt = 0;
+ cnt += buf_copy(&pfd[cnt], &in_buf);
+ cnt += buf_copy(&pfd[cnt], &out_buf);
+ }
+
+ close(cli_fd);
+}
+
+int redirect_execute(const char *service, const char *path)
+{
+ struct stat sb;
+ char *sock_name, *s, *repo_name;
+
+ /* Exists and is a directory? Assume the caller is
+ * the named service and can directly access this
+ * location itself, without redirect sockets.
+ */
+ if (!stat(path, &sb) && S_ISDIR(sb.st_mode))
+ return 0;
+ s = strrchr(path, '/');
+ if (!s)
+ return 0;
+
+ sock_name = xstrdup(path);
+ s = sock_name + (s - path);
+ *s = 0;
+ repo_name = s + 1;
+
+ for (;;) {
+ if (!stat(sock_name, &sb)) {
+ if (S_ISDIR(sb.st_mode))
+ break;
+ if (S_ISSOCK(sb.st_mode)) {
+ execute(service, sock_name, repo_name);
+ free(sock_name);
+ return 1;
+ }
+ }
+
+ s = strrchr(sock_name, '/');
+ if (!s || s == sock_name)
+ break;
+
+ *s = 0;
+ *(--repo_name) = '/';
+ repo_name = s + 1;
+ }
+
+ /* Assume redirection shouldn't be applied and that the
+ * path is also completely incorrect. Allow the caller
+ * to handle this error condition as before.
+ */
+ free(sock_name);
+ return 0;
+}
+
+void redirect_unlisten(int srv_fd, const char *path)
+{
+ close(srv_fd);
+ unlink(path);
+}
diff --git a/daemon-redirect.h b/daemon-redirect.h
new file mode 100644
index 0000000..5d85e10
--- /dev/null
+++ b/daemon-redirect.h
@@ -0,0 +1,12 @@
+#ifndef DAEMON_REDIRECT_H
+#define DAEMON_REDIRECT_H
+
+#include "git-compat-util.h"
+
+extern int redirect_listen(const char *path);
+extern int redirect_accept(int, uid_t *);
+extern void redirect_unlisten(int, const char *);
+
+extern int redirect_execute(const char *, const char *);
+
+#endif
diff --git a/daemon.c b/daemon.c
index 41a60af..6c4897d 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "daemon-redirect.h"
#include "pkt-line.h"
#include "exec_cmd.h"
#include "interpolate.h"
@@ -16,6 +17,7 @@
static int log_syslog;
static int verbose;
static int reuseaddr;
+static char *listen_addr;
static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--export-all]\n"
@@ -25,7 +27,7 @@ static const char daemon_usage[] =
" [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\n"
-" [--inetd | [--listen=host_or_ipaddr] [--port=n]\n"
+" [--inetd | [--listen=host_or_ipaddr_or_path] [--port=n]\n"
" [--user=user [--group=group]]\n"
" [directory...]";
@@ -318,13 +320,24 @@ static int git_daemon_config(const char *var, const char *value)
return 0;
}
-static int run_service(struct interp *itable, struct daemon_service *service)
+static int run_service(struct interp *itable, struct daemon_service *service, uid_t uid)
{
const char *path;
+ struct passwd *pw = getpwuid(uid);
int enabled = service->enabled;
+ char *pw_name;
- loginfo("Request %s for '%s'",
+ if (pw)
+ pw_name = xstrdup(pw->pw_name);
+ else {
+ pw_name = xmalloc(64);
+ snprintf(pw_name, 64, "uid %u", uid);
+ }
+ setenv("GIT_REMOTE_USER", pw_name, 1);
+
+ loginfo("Request %s for %s in '%s'",
service->name,
+ pw_name,
itable[INTERP_SLOT_DIR].value);
if (!enabled && !service->overridable) {
@@ -530,7 +543,7 @@ static void fill_in_extra_table_entries(struct interp *itable)
}
-static int execute(struct sockaddr *addr)
+static int execute(struct sockaddr *addr, uid_t uid)
{
static char line[1000];
int pktlen, len, i;
@@ -595,7 +608,7 @@ static int execute(struct sockaddr *addr)
*/
interp_set_entry(interp_table,
INTERP_SLOT_DIR, line + namelen + 5);
- return run_service(interp_table, s);
+ return run_service(interp_table, s, uid);
}
}
@@ -636,7 +649,10 @@ static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
{
live_child[idx].pid = pid;
live_child[idx].addrlen = addrlen;
- memcpy(&live_child[idx].address, addr, addrlen);
+ if (addr)
+ memcpy(&live_child[idx].address, addr, addrlen);
+ else
+ memset(&live_child[idx].address, 0, sizeof(live_child[idx].address));
}
/*
@@ -726,7 +742,7 @@ static void check_max_connections(void)
}
}
-static void handle(int incoming, struct sockaddr *addr, int addrlen)
+static void handle(int incoming, struct sockaddr *addr, int addrlen, uid_t uid)
{
pid_t pid = fork();
@@ -749,7 +765,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
dup2(incoming, 1);
close(incoming);
- exit(execute(addr));
+ exit(execute(addr, uid));
}
static void child_handler(int signo)
@@ -790,7 +806,7 @@ static int set_reuse_addr(int sockfd)
#ifndef NO_IPV6
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int socksetup(int listen_port, int **socklist_p)
{
int socknum = 0, *socklist = NULL;
int maxfd = -1;
@@ -864,7 +880,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
#else /* NO_IPV6 */
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int socksetup(int listen_port, int **socklist_p)
{
struct sockaddr_in sin;
int sockfd;
@@ -912,8 +928,15 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
#endif
+static volatile int end_daemon;
+static void term_handler(int signo)
+{
+ end_daemon = 1;
+}
+
static int service_loop(int socknum, int *socklist)
{
+ uid_t uid = getuid();
struct pollfd *pfd;
int i;
@@ -924,9 +947,7 @@ static int service_loop(int socknum, int *socklist)
pfd[i].events = POLLIN;
}
- signal(SIGCHLD, child_handler);
-
- for (;;) {
+ while (!end_daemon) {
int i;
if (poll(pfd, socknum, -1) < 0) {
@@ -950,13 +971,36 @@ static int service_loop(int socknum, int *socklist)
case ECONNABORTED:
continue;
default:
- die("accept returned %s", strerror(errno));
+ error("accept returned %s", strerror(errno));
+ return 1;
}
}
- handle(incoming, (struct sockaddr *)&ss, sslen);
+ handle(incoming, (struct sockaddr *)&ss, sslen, uid);
}
}
}
+ return 0;
+}
+
+static int redirect_loop(int srv_fd)
+{
+ while (!end_daemon) {
+ uid_t uid;
+ int incoming = redirect_accept(srv_fd, &uid);
+ if (incoming < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ case ECONNABORTED:
+ continue;
+ default:
+ error("accept returned %s", strerror(errno));
+ return 1;
+ }
+ }
+ handle(incoming, NULL, 0, uid);
+ }
+ return 0;
}
/* if any standard file descriptor is missing open it to /dev/null */
@@ -998,27 +1042,51 @@ static void store_pid(const char *path)
die("failed to write pid file %s: %s", path, strerror(errno));
}
-static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
+static int serve(int listen_port, struct passwd *pass, gid_t gid)
{
- int socknum, *socklist;
-
- socknum = socksetup(listen_addr, listen_port, &socklist);
- if (socknum == 0)
- die("unable to allocate any listen sockets on host %s port %u",
- listen_addr, listen_port);
+ int socknum, *socklist, status;
+ int is_redirect = is_absolute_path(listen_addr);
+ struct sigaction sa;
+
+ if (is_redirect) {
+ socknum = 1;
+ socklist = xmalloc(sizeof(int));
+ socklist[0] = redirect_listen(listen_addr);
+ } else {
+ socknum = socksetup(listen_port, &socklist);
+ if (socknum == 0)
+ die("unable to allocate any listen sockets"
+ " on host %s port %u",
+ listen_addr, listen_port);
+ }
if (pass && gid &&
(initgroups(pass->pw_name, gid) || setgid (gid) ||
setuid(pass->pw_uid)))
die("cannot drop privileges");
- return service_loop(socknum, socklist);
+ signal(SIGCHLD, child_handler);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = term_handler;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ if (is_redirect) {
+ status = redirect_loop(socklist[0]);
+ redirect_unlisten(socklist[0], listen_addr);
+ } else {
+ status = service_loop(socknum, socklist);
+ while (--socknum >= 0)
+ close(socklist[socknum]);
+ }
+ return status;
}
int main(int argc, char **argv)
{
int listen_port = 0;
- char *listen_addr = NULL;
int inetd_mode = 0;
const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
int detach = 0;
@@ -1038,9 +1106,13 @@ int main(int argc, char **argv)
if (!prefixcmp(arg, "--listen=")) {
char *p = arg + 9;
char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
- while (*p)
- *ph++ = tolower(*p++);
- *ph = 0;
+ if (is_absolute_path(p))
+ strcpy(ph, p);
+ else {
+ while (*p)
+ *ph++ = tolower(*p++);
+ *ph = 0;
+ }
continue;
}
if (!prefixcmp(arg, "--port=")) {
@@ -1154,6 +1226,8 @@ int main(int argc, char **argv)
if (inetd_mode && (listen_port || listen_addr))
die("--listen= and --port= are incompatible with --inetd");
+ else if (listen_port && listen_addr && is_absolute_path(listen_addr))
+ die("--listen path and --port are incompatible");
else if (listen_port == 0)
listen_port = DEFAULT_GIT_PORT;
@@ -1194,7 +1268,7 @@ int main(int argc, char **argv)
if (getpeername(0, peer, &slen))
peer = NULL;
- return execute(peer);
+ return execute(peer, getuid());
}
if (detach)
@@ -1205,5 +1279,5 @@ int main(int argc, char **argv)
if (pid_file)
store_pid(pid_file);
- return serve(listen_addr, listen_port, pass, gid);
+ return serve(listen_port, pass, gid);
}
diff --git a/git-compat-util.h b/git-compat-util.h
index b6ef544..892b7f9 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -74,6 +74,7 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
+#include <sys/un.h>
#include <netdb.h>
#include <pwd.h>
#include <inttypes.h>
diff --git a/receive-pack.c b/receive-pack.c
index 3267495..6970979 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -2,6 +2,7 @@
#include "pack.h"
#include "refs.h"
#include "pkt-line.h"
+#include "daemon-redirect.h"
#include "run-command.h"
#include "exec_cmd.h"
#include "commit.h"
@@ -469,6 +470,8 @@ int main(int argc, char **argv)
if (!dir)
usage(receive_pack_usage);
+ if (redirect_execute("receive-pack", dir))
+ return 0;
if (!enter_repo(dir, 0))
die("'%s': unable to chdir or not a git archive", dir);
diff --git a/upload-pack.c b/upload-pack.c
index 7e04311..8102126 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "refs.h"
#include "pkt-line.h"
+#include "daemon-redirect.h"
#include "sideband.h"
#include "tag.h"
#include "object.h"
@@ -622,6 +623,8 @@ int main(int argc, char **argv)
usage(upload_pack_usage);
dir = argv[i];
+ if (redirect_execute("upload-pack", dir))
+ return 0;
if (!enter_repo(dir, strict))
die("'%s': unable to chdir or not a git archive", dir);
if (is_repository_shallow())
--
1.5.4.rc4.1142.gf5a97
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 10:39 [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
@ 2008-01-27 14:04 ` Johannes Schindelin
2008-01-27 17:32 ` Shawn O. Pearce
2008-01-27 22:56 ` Junio C Hamano
1 sibling, 1 reply; 16+ messages in thread
From: Johannes Schindelin @ 2008-01-27 14:04 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
Hi,
On Sun, 27 Jan 2008, Shawn O. Pearce wrote:
> ## Owner (not jdoe)
> ##
> cat >foo.git/hooks/update <<'EOF'
> #!/bin/sh
> test -z "$GIT_REMOTE_USER" || exit
> case "$GIT_REMOTE_USER" in
> jdoe) exit 0;;
> spearce) exit 0;;
> *) exit 1
> esac
> EOF
> chmod u+x foo.git/hooks/update
> chmod 700 foo.git
>
> git daemon \
> --export-all \
> --enable=receive-pack \
> --base=`pwd` \
> --listen=/tmp/shawn-git
>
> ## Other User
> ##
> git push jdoe@server:/tmp/shawn-git/foo.git master
I probably miss something, but if you already go through SSH, the $USER is
set appropriately, no?
So you could do without git-daemon entirely, and replace the
GIT_REMOTE_USER variable in the hook by USER.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 14:04 ` Johannes Schindelin
@ 2008-01-27 17:32 ` Shawn O. Pearce
2008-01-27 18:51 ` Johannes Schindelin
2008-01-28 8:14 ` Dmitry Potapov
0 siblings, 2 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-27 17:32 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> On Sun, 27 Jan 2008, Shawn O. Pearce wrote:
>
> > ## Owner (not jdoe)
> > ##
> > cat >foo.git/hooks/update <<'EOF'
> > #!/bin/sh
> > test -z "$GIT_REMOTE_USER" || exit
> > case "$GIT_REMOTE_USER" in
> > jdoe) exit 0;;
> > spearce) exit 0;;
> > *) exit 1
> > esac
> > EOF
> > chmod u+x foo.git/hooks/update
> > chmod 700 foo.git
> >
> > git daemon \
> > --export-all \
> > --enable=receive-pack \
> > --base=`pwd` \
> > --listen=/tmp/shawn-git
> >
> > ## Other User
> > ##
> > git push jdoe@server:/tmp/shawn-git/foo.git master
>
> I probably miss something, but if you already go through SSH, the $USER is
> set appropriately, no?
Sure, $USER is set. For "jdoe". But due to the "chmod 700 foo.git"
above jdoe isn't actually allowed access to the repository directory.
So it doesn't matter what $USER is set to, jdoe cannot get to the
files of the repository.
> So you could do without git-daemon entirely, and replace the
> GIT_REMOTE_USER variable in the hook by USER.
No, you can't.
If you give write access to a repository such that a user can push into
it, there is little point in using the update hook to control access to
the repository. Why? Because anyone with write access can just use the
standard Git commands (e.g. `git --git-dir=foo.git branch -D next`)
to maniplate the repository in ways that the update hook wouldn't like.
So if you want to give out write access, but have it be limited
to what `git receive-pack` will permit (especially when coupled
with an update hook like contrib/hooks/update-paranoid) you need
to limit what a user can do to *only* executing git-receive-pack,
but you also need to allow that receive-pack to actually have write
permission on the repository.
So you come down to four options:
1) Make git-receive-pack setuid to the repository owner.
This is uh, ugly.
2) Use the SSH key feature to have remote users login as
the repository owner, but use the authorized_keys file
to force them to only execute git-shell.
This is uh, ugly, especially with 50+ users.
3) Export git-daemon over TCP and --enable=receive-pack.
This doesn't get us any authentication. Sure the
user is limited to what the update hook allows, but
the update hook has no way to trust who the remote
peer is. You might as well not bother.
4) Add full user authentication to git-daemon and then do #3.
The user authentication can provide data down into the update
hook, such as by setting the $GIT_REMOTE_USER environment
variable. That's basically this change, except I'm using bog
standard SSH to perform the authentication for me.
If we don't do something like this then I think we need to teach
git-daemon how to accept SSL connections, and then once you give it
SSL you need to implement peer authentication by certificates, and
then have git_connect() in connect.c also implement setting up SSL
connections, and doing peer authentication with certificates. Ick.
Relying on SSH is the better approach in my opinion. It comes with
a set of well trusted servers (OpenSSH) and user client tools that
many users and administrators already understand (e.g. ssh-agent).
Someone on #git yesterday mentioned that a problem half-deferred
(user authentication) is a problem half-solved. I've half-deferred
the authentication problem to SSH, much as we already have it
deferred to SSH... :-)
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 17:32 ` Shawn O. Pearce
@ 2008-01-27 18:51 ` Johannes Schindelin
2008-01-28 0:54 ` Shawn O. Pearce
2008-01-28 8:14 ` Dmitry Potapov
1 sibling, 1 reply; 16+ messages in thread
From: Johannes Schindelin @ 2008-01-27 18:51 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
Hi,
On Sun, 27 Jan 2008, Shawn O. Pearce wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > On Sun, 27 Jan 2008, Shawn O. Pearce wrote:
> >
> > > ## Owner (not jdoe)
> > > ##
> > > cat >foo.git/hooks/update <<'EOF'
> > > #!/bin/sh
> > > test -z "$GIT_REMOTE_USER" || exit
> > > case "$GIT_REMOTE_USER" in
> > > jdoe) exit 0;;
> > > spearce) exit 0;;
> > > *) exit 1
> > > esac
> > > EOF
> > > chmod u+x foo.git/hooks/update
> > > chmod 700 foo.git
> > >
> > > git daemon \
> > > --export-all \
> > > --enable=receive-pack \
> > > --base=`pwd` \
> > > --listen=/tmp/shawn-git
> > >
> > > ## Other User
> > > ##
> > > git push jdoe@server:/tmp/shawn-git/foo.git master
> >
> > I probably miss something, but if you already go through SSH, the
> > $USER is set appropriately, no?
>
> Sure, $USER is set. For "jdoe". But due to the "chmod 700 foo.git"
> above jdoe isn't actually allowed access to the repository directory. So
> it doesn't matter what $USER is set to, jdoe cannot get to the files of
> the repository.
Ah, that's what I missed. I thought you already used git-shell, and did
not really read the chmod part.
> So if you want to give out write access, but have it be limited to what
> `git receive-pack` will permit (especially when coupled with an update
> hook like contrib/hooks/update-paranoid) you need to limit what a user
> can do to *only* executing git-receive-pack, but you also need to allow
> that receive-pack to actually have write permission on the repository.
>
> So you come down to four options:
>
> 1) Make git-receive-pack setuid to the repository owner.
> This is uh, ugly.
Concur.
> 2) Use the SSH key feature to have remote users login as
> the repository owner, but use the authorized_keys file
> to force them to only execute git-shell.
> This is uh, ugly, especially with 50+ users.
Slight variation: do not permit other users access to your machine, except
via git-shell. Then you don't need chmod 0700.
> 3) Export git-daemon over TCP and --enable=receive-pack.
> This doesn't get us any authentication. Sure the
> user is limited to what the update hook allows, but
> the update hook has no way to trust who the remote
> peer is. You might as well not bother.
Right. --enable=receive-pack was meant for environments where you have to
trust everybody anyway (think "Visual" SourceSafe, or PVCS with
repositories on network shares).
> 4) Add full user authentication to git-daemon and then do #3.
> The user authentication can provide data down into the update
> hook, such as by setting the $GIT_REMOTE_USER environment
> variable. That's basically this change, except I'm using bog
> standard SSH to perform the authentication for me.
AFAIR the plan was to keep git-daemon as simple and stupid as possible; in
particular _not_ to add any authentication.
> If we don't do something like this then I think we need to teach
> git-daemon how to accept SSL connections, and then once you give it SSL
> you need to implement peer authentication by certificates, and then have
> git_connect() in connect.c also implement setting up SSL connections,
> and doing peer authentication with certificates. Ick.
I never tried anything like this, but it should not be _that_ difficult,
should it? Of course, I should read up on it before I say something about
it first ;-)
Ciao,
Dscho
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 10:39 [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
2008-01-27 14:04 ` Johannes Schindelin
@ 2008-01-27 22:56 ` Junio C Hamano
2008-01-28 0:16 ` git-daemon is insecure? (was: [RFC] Secure central repositories) Shawn O. Pearce
2008-01-28 0:47 ` [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
1 sibling, 2 replies; 16+ messages in thread
From: Junio C Hamano @ 2008-01-27 22:56 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
"Shawn O. Pearce" <spearce@spearce.org> writes:
> This isn't anywhere near ready for application, but I'm floating
> it out there to see what people think. Its a cool new feature that
> will certainly *not* be in 1.5.4. :-)
>
> In a central repository configuration users may not have access
> to write new objects into a Git repository, or to edit the refs,
> especially if the repository is being protected by an update hook
> (e.g. contrib/hooks/updated-paranoid).
Sorry, but I am puzzled about what this assumption is trying to
achieve here.
If the configuration is based on central repository model,
wouldn't the users who are participating in the project have
write access to the repository by being in the project's group
and the repository initialized with core.sharedrepository=true?
> This change allows any repository owner to setup a git-daemon
> that other users on the same host can connect through to perform
> upload-pack or receive-pack.
My reading of this is that it creates a backdoor for people who
cannot (either "are not allowed to", or "cannot be bothered to")
create and maintain project specific access control by the
traditional means of filesystem access control based on user
groups, by allowing others to run controlled stuff under the
repository owner's uid. In addition to having to worry about
the in-repo data properly being protected from people outside
the group, you now need to worry about the access through that
backdoor does not extend outside of the repository. E.g. the
repository owner's $HOME that is outside the repository would be
writable that owner, but is not meant to be accessible by
project participants. If you allow others to "run as" you, the
only thing that forbids that process running as you from
accessing $HOME is an additional audit of git-daemon and the
programs it spawns.
In short, my initial reaction to this is not very supportive,
not because of the way it is coded but because of its security
design.
But I may probably have misread the intention.
^ permalink raw reply [flat|nested] 16+ messages in thread
* git-daemon is insecure? (was: [RFC] Secure central repositories)
2008-01-27 22:56 ` Junio C Hamano
@ 2008-01-28 0:16 ` Shawn O. Pearce
2008-01-28 3:00 ` git-daemon is insecure? Junio C Hamano
2008-01-28 0:47 ` [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
1 sibling, 1 reply; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-28 0:16 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
> > This change allows any repository owner to setup a git-daemon
> > that other users on the same host can connect through to perform
> > upload-pack or receive-pack.
>
> My reading of this is that it creates a backdoor for people who
[...]
> In addition to having to worry about
> the in-repo data properly being protected from people outside
> the group, you now need to worry about the access through that
> backdoor does not extend outside of the repository. E.g. the
> repository owner's $HOME that is outside the repository would be
> writable that owner, but is not meant to be accessible by
> project participants. If you allow others to "run as" you, the
> only thing that forbids that process running as you from
> accessing $HOME is an additional audit of git-daemon and the
> programs it spawns.
So you are partially suggesting that git-daemon isn't thought to
be secure, and that anything readable by the user that git-daemon
is running as is fully exposed to the public Internet. So the
access control attempts relating to --base-path or the check for
git-daemon-export-ok shouldn't really be trusted or relied upon.
If that really is the case, perhaps git-daemon should be audited
and hardened further. Last I checked, we encouraged people to run
it to offer anonymous access to repositories, and the documentation
suggests there are publishing access controls that actually work.
If those controls cannot be trusted then we shouldn't encourage
running git-daemon on untrusted networks.
With regards to this patch, yes, you can export your entire $HOME
and maybe expose things you shouldn't or didn't want to. But even
without git installed you could do this:
cp /bin/bash /tmp/be-like-mike
chown $USER /tmp/be-like-mike
chmod 777 /tmp/be-like-mike
chmod u+s /tmp/be-like-mike
wall "try out /tmp/be-like-mike today"
but why would anyone do something that foolish? UNIX provides the
tools to do this, because there are cases where it can be useful,
but really, you have to be nuts to export all of $HOME.
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 22:56 ` Junio C Hamano
2008-01-28 0:16 ` git-daemon is insecure? (was: [RFC] Secure central repositories) Shawn O. Pearce
@ 2008-01-28 0:47 ` Shawn O. Pearce
2008-01-28 7:25 ` Junio C Hamano
1 sibling, 1 reply; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-28 0:47 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
>
> > This isn't anywhere near ready for application, but I'm floating
> > it out there to see what people think. Its a cool new feature that
> > will certainly *not* be in 1.5.4. :-)
> >
> > In a central repository configuration users may not have access
> > to write new objects into a Git repository, or to edit the refs,
> > especially if the repository is being protected by an update hook
> > (e.g. contrib/hooks/updated-paranoid).
>
> Sorry, but I am puzzled about what this assumption is trying to
> achieve here.
>
> If the configuration is based on central repository model,
> wouldn't the users who are participating in the project have
> write access to the repository by being in the project's group
> and the repository initialized with core.sharedrepository=true?
Hmm. core.sharedrepository is sometimes a bad solution.
core.sharedrepository means I need to give write access to both the
refs database and the object database to all members of the project.
Some of whom may not be able to be trusted with tools like "rm",
but who need real shell access to that system anyway. And sometimes
management won't allow users to have two accounts on the same system
(one that is fixed to git-shell, and one that has a real shell)
because the world would implode if a user was given two different
accounts for two different access purposes. I have no idea why that
would happen, but someone paid 3x what I earn has figured that out.
Last I checked how UNIX filesystem access controls work, Git's
core.sharedrepository cannot possibly prevent a user from doing
something like this:
cd $repo_path
git log
... go to lunch ...
rm -rf *
git log
... bitch about how crappy git is ...
Now any "real" version control and SQL database system allows the
administrator to restrict access to the database to avoid such
mistakes. CVS has pserver; SVN can use Apache HTTPd or its own
server; Perforce has its own server. PostgreSQL, Oracle, Informix,
DB2, even MySQL don't allow users to directly read or modify the
database files but instead ask them to go through authenticated
socket based interfaces.
Under a DSCM one would say the central model is insane, and that
every user should have their own repository, with write access
limited to only that user. Obviously this is the model that
kernel.org uses for kernel development, and that git itself uses
for git development.
The problem is the purely distributed model falls apart when you have
50 "Aunt Tillies" making changes to the same 30 files at around the
same time. Its bad enough that they have to have a local clone and
push and fetch to share their changes. Trying to explain that you
need to fetch+merge from Jane right now and Bob 3 minutes later,
then back to Jane to get the changes you are working on in parallel
is sheer chaos. Eyes gloss over and management declares "Git is
crap; it cannot possibly be used in the enterprise".
How do you setup 50 URLs into all 50 user's .git/config? When a new
user joins the project how do you get their URL into all existing
user's trees? Its total chaos in the cube farm as they shout back
and forth "Did you get Bob's changes? Jane's? Oh, maybe you didn't
get Sally's too and that's why you aren't seeing X in there".
At day-job I manage two completely different workflows, but both
are based upon Git.
Real developers who hack out program source code use a model much
like kernel.org. Code is developed on topic branches, code is
reviewed on topic branches at the individual change level, and code
is merged from a developer's topic branch by a maintainer into a
master branch. For convience sake we store all topic branches in a
single central repository, so everyone just has to have the "origin"
URL in their local repositories. We could deal with individual
developer repos like kernel.org does. We choose not to simply
because we also have to handle the next case, and its easier to
not have individual developer repos.
Aunt Tillies (who far out number real developers) edit small text
files through a fancy GUI tool. These folks don't really care
about versions, topic branches, and really don't want to know.
All they know is they have to edit "Foo.data_file" but to do so
they need the changes just made to "Bar.data_file" 5 minutes ago.
Usually they don't even know if Bob, Sally, Jane or Nick made that
change, they just know they need it. And their collective changes
(from all 50 Aunt Tillies) all have to somehow wind up in the same
Git branch at the end of the day so a real developer/maintainer
can pull it into a product build.
I've lived through the daily fires of these workflows over the past
year and a half. Its mostly settled out to something that works very
well for us, but its heavily based upon this concept of a central,
shared repository. And to keep the auditors and management happy
I cannot allow "rm -rf *" to be executed by a user who happens to
have push access to that same repository.
We're not the only Git user that has a shared repository. Doesn't
X.org use a shared repository model? I'm guessing that since they
are an open source project they have less concerns about the "rm
-rf *" case. Wasn't the receive-pack service added to git-daemon
to allow users to push into a repository, but not actually have
write access to its filesystem? Obviously someone else other than
just me wants to safeguard the repository.
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 18:51 ` Johannes Schindelin
@ 2008-01-28 0:54 ` Shawn O. Pearce
0 siblings, 0 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-28 0:54 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> On Sun, 27 Jan 2008, Shawn O. Pearce wrote:
> >
> > Sure, $USER is set. For "jdoe". But due to the "chmod 700 foo.git"
> > above jdoe isn't actually allowed access to the repository directory. So
> > it doesn't matter what $USER is set to, jdoe cannot get to the files of
> > the repository.
>
> Ah, that's what I missed. I thought you already used git-shell, and did
> not really read the chmod part.
No, I'm not using git-shell. I'm actually currently using a setuid
git-receive-pack, which we've both agreed is horribly ugly. I want
to get away from that mess.
> > 2) Use the SSH key feature to have remote users login as
> > the repository owner, but use the authorized_keys file
> > to force them to only execute git-shell.
> > This is uh, ugly, especially with 50+ users.
>
> Slight variation: do not permit other users access to your machine, except
> via git-shell. Then you don't need chmod 0700.
This isn't an option. At least 10% of the users need a real shell
on this system, but cannot be trusted to not directly edit the
repository. I'm also not able to get them different user accounts
(one for git-shell, one for normal shell) because giving the same
human two different user accounts on the same UNIX system will
cause the world to explode. At least according to some management
people who get paid 3x what I get paid.
Of course, note those same people have also said that a SAMBA server
cannot run on a system unless it is a SAMBA server. Catch-22.
You cannot run SAMBA unless you are already running SAMBA. :-\
> > 4) Add full user authentication to git-daemon and then do #3.
> > The user authentication can provide data down into the update
> > hook, such as by setting the $GIT_REMOTE_USER environment
> > variable. That's basically this change, except I'm using bog
> > standard SSH to perform the authentication for me.
>
> AFAIR the plan was to keep git-daemon as simple and stupid as possible; in
> particular _not_ to add any authentication.
Yup. I think its smart. Defer authentication off to the standard OS
tools, so we don't have to deal with it in git itself.
Yet I'm offering a patch for comment that adds some level of
authentication to git-daemon. At least it still just relies on
UNIX uids and doesn't actually try to link to PAM. :-)
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: git-daemon is insecure?
2008-01-28 0:16 ` git-daemon is insecure? (was: [RFC] Secure central repositories) Shawn O. Pearce
@ 2008-01-28 3:00 ` Junio C Hamano
2008-01-28 3:20 ` Shawn O. Pearce
0 siblings, 1 reply; 16+ messages in thread
From: Junio C Hamano @ 2008-01-28 3:00 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
"Shawn O. Pearce" <spearce@spearce.org> writes:
> With regards to this patch, yes, you can export your entire $HOME
> and maybe expose things you shouldn't or didn't want to.
That was not what I meant. git-daemon running as nobody.project
will allow read access to project group's files, and the
whitelisting and --base-path are ways to limit it to files that
are in the repository. But the process still has the power to
read files outside that can be read nobody user or project
group, the only thing needed is for git-daemon and whatever it
spawn to have bugs.
But the point is that "power to read files outside" is still
limited to nobody.project, even if there are such bugs to allow
it escape the whitelist/base-path jail. It won't extend to
anybody's $HOME.
If you run git-daemon as spearce.spearce, you cannot rely on
that built-in limitation.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: git-daemon is insecure?
2008-01-28 3:00 ` git-daemon is insecure? Junio C Hamano
@ 2008-01-28 3:20 ` Shawn O. Pearce
0 siblings, 0 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-28 3:20 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
>
> > With regards to this patch, yes, you can export your entire $HOME
> > and maybe expose things you shouldn't or didn't want to.
>
> That was not what I meant. git-daemon running as nobody.project
> will allow read access to project group's files, and the
> whitelisting and --base-path are ways to limit it to files that
> are in the repository. But the process still has the power to
> read files outside that can be read nobody user or project
> group, the only thing needed is for git-daemon and whatever it
> spawn to have bugs.
>
> But the point is that "power to read files outside" is still
> limited to nobody.project, even if there are such bugs to allow
> it escape the whitelist/base-path jail. It won't extend to
> anybody's $HOME.
>
> If you run git-daemon as spearce.spearce, you cannot rely on
> that built-in limitation.
Sure. Which is why I was planning on running git-daemon as
gitadmin.gitadmin, with all central repos owned by gitadmin,
and basically nothing else at all.
I can just as easily start lighthttpd on $HOME. Or Apache.
Both are insane.
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-28 0:47 ` [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
@ 2008-01-28 7:25 ` Junio C Hamano
2008-01-28 7:51 ` Shawn O. Pearce
2008-01-28 7:56 ` Shawn O. Pearce
0 siblings, 2 replies; 16+ messages in thread
From: Junio C Hamano @ 2008-01-28 7:25 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: git
"Shawn O. Pearce" <spearce@spearce.org> writes:
> Hmm. core.sharedrepository is sometimes a bad solution.
>
> core.sharedrepository means I need to give write access to both the
> refs database and the object database to all members of the project.
> Some of whom may not be able to be trusted with tools like "rm",
> but who need real shell access to that system anyway. And sometimes
> management won't allow users to have two accounts on the same system
> (one that is fixed to git-shell, and one that has a real shell)
> because the world would implode if a user was given two different
> accounts for two different access purposes.
Ok, that was the motiviation I did not get from your original
message. It begins to make sense somewhat.
Another approach to do the same I can think of, without having
to add 50 new accounts for 50 users, would be to collect a ssh
key from each of these 50 users, and have 1 line per user in the
authorized_keys file of gitadmin.gitadmin user (who owns the
repository with the paranoia hook that decides the authorization
aspect of the repository). The authentication would come from
the environment="Name=value" option in the authorized_keys file.
Each of your aunt tillies can push or fetch over ssh using the
key she has in the gitadmin.gitadmin's authorized_keys file.
I suspect the "hackiness" factor from the aesthetics viewpoint
is probably about the same, but this would work with the current
code without patches, no?
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-28 7:25 ` Junio C Hamano
@ 2008-01-28 7:51 ` Shawn O. Pearce
2008-01-28 14:23 ` Asheesh Laroia
2008-01-28 7:56 ` Shawn O. Pearce
1 sibling, 1 reply; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-28 7:51 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
>
> > Hmm. core.sharedrepository is sometimes a bad solution.
> >
> > core.sharedrepository means I need to give write access to both the
> > refs database and the object database to all members of the project.
> > Some of whom may not be able to be trusted with tools like "rm",
> > but who need real shell access to that system anyway. And sometimes
> > management won't allow users to have two accounts on the same system
> > (one that is fixed to git-shell, and one that has a real shell)
> > because the world would implode if a user was given two different
> > accounts for two different access purposes.
>
> Ok, that was the motiviation I did not get from your original
> message. It begins to make sense somewhat.
>
> Another approach to do the same I can think of, without having
> to add 50 new accounts for 50 users, would be to collect a ssh
> key from each of these 50 users, and have 1 line per user in the
> authorized_keys file of gitadmin.gitadmin user (who owns the
> repository with the paranoia hook that decides the authorization
> aspect of the repository). The authentication would come from
> the environment="Name=value" option in the authorized_keys file.
> Each of your aunt tillies can push or fetch over ssh using the
> key she has in the gitadmin.gitadmin's authorized_keys file.
Yea. The downside to this is we have to maintain that
authorized_keys file. Today each user can generate their
own SSH key and upload to their own authorized_keys file.
I've had enough cases of users losing their SSH key and
needing to recreate it that I'd rather not have to manage
a 50 user long authorized_keys file.
I'm also not sure of what the performance implication is to SSH for
authentication with 50 public keys in a single file. Does it slow
down as its running a check against each key, or is there something
smarter to filter the keys? From the description of the format in
the OpenSSH manpage it doesn't look like its possible other than
to probe every key in turn for every authentication attempt.
In other words, the authorized_keys is a nice feature, but it seems
like its more useful for a remote backup job logging in as root, or a
"vacation mode" where a coworker is permitted to execute a specific
command while you are away. But maybe I'm missing something.
> I suspect the "hackiness" factor from the aesthetics viewpoint
> is probably about the same, but this would work with the current
> code without patches, no?
Yes, it would, obviously. But it has the downside of needing to
manage a single common authorized_keys file. Which is something
I think I'd like to avoid.
It also doesn't do anything about upload-pack, as that has no hook
to perform authorization. Of course just adding some sort of ref
filtering hook to upload-pack is still a smaller patch than adding
all this UNIX socket redirection and an upload-pack hook. :)
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-28 7:25 ` Junio C Hamano
2008-01-28 7:51 ` Shawn O. Pearce
@ 2008-01-28 7:56 ` Shawn O. Pearce
1 sibling, 0 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-28 7:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
>
> > Hmm. core.sharedrepository is sometimes a bad solution.
> >
> > core.sharedrepository means I need to give write access to both the
> > refs database and the object database to all members of the project.
> > Some of whom may not be able to be trusted with tools like "rm",
> > but who need real shell access to that system anyway. And sometimes
> > management won't allow users to have two accounts on the same system
> > (one that is fixed to git-shell, and one that has a real shell)
> > because the world would implode if a user was given two different
> > accounts for two different access purposes.
...
> Another approach to do the same I can think of, without having
> to add 50 new accounts for 50 users, would be to collect a ssh
> key from each of these 50 users,
Also, our network of servers and desktops is actually now managed
entirely through Active Directory. So all possible users already
have accounts on every server. Passwords are synchronized across
the entire network.
In other words, all of the management overheads associated with
user accounts has already been paid. Centralized SSH/SSL/PGP key
management is only adding an additional burden. For my day-job
anyway.
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-27 17:32 ` Shawn O. Pearce
2008-01-27 18:51 ` Johannes Schindelin
@ 2008-01-28 8:14 ` Dmitry Potapov
1 sibling, 0 replies; 16+ messages in thread
From: Dmitry Potapov @ 2008-01-28 8:14 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: Johannes Schindelin, git
On Sun, Jan 27, 2008 at 12:32:13PM -0500, Shawn O. Pearce wrote:
>
> So you come down to four options:
<snip>
How about gitosis? It requires only one extra user (usually called git),
which is the owner of all repos. This user has git-shell as its login
shell. All users are authorized by their ssh keys. The configuration and
kyes are stored in the special repo called gitosis-admin. You can define
what users to what repositories have read or write access. This is done
by adding a user to one or more groups defined in gitosis configuration.
You can have as much groups as you want. The default configuration looks
like this:
[group gitosis-admin]
writable = gitosis-admin
members = your-name
It defines the gitosis-admin group, member of which can write to the
gitosis-admin repo, and you are member of that group.
WRRNING: I have not used gitosis myself, but it looks worthy of a try.
Dmitry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-28 7:51 ` Shawn O. Pearce
@ 2008-01-28 14:23 ` Asheesh Laroia
2008-01-29 3:11 ` Shawn O. Pearce
0 siblings, 1 reply; 16+ messages in thread
From: Asheesh Laroia @ 2008-01-28 14:23 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: Junio C Hamano, git
On Mon, 28 Jan 2008, Shawn O. Pearce wrote:
> Junio C Hamano <gitster@pobox.com> wrote:
>> "Shawn O. Pearce" <spearce@spearce.org> writes:
>>
>>> Hmm. core.sharedrepository is sometimes a bad solution.
>>>
>>> core.sharedrepository means I need to give write access to both the
>>> refs database and the object database to all members of the project.
>>> Some of whom may not be able to be trusted with tools like "rm", but
>>> who need real shell access to that system anyway. And sometimes
>>> management won't allow users to have two accounts on the same system
>>> (one that is fixed to git-shell, and one that has a real shell)
>>> because the world would implode if a user was given two different
>>> accounts for two different access purposes.
>>
>> Ok, that was the motiviation I did not get from your original message.
>> It begins to make sense somewhat.
>>
>> Another approach to do the same I can think of, without having to add
>> 50 new accounts for 50 users, would be to collect a ssh key from each
>> of these 50 users, and have 1 line per user in the authorized_keys file
>> of gitadmin.gitadmin user (who owns the repository with the paranoia
>> hook that decides the authorization aspect of the repository). The
>> authentication would come from the environment="Name=value" option in
>> the authorized_keys file. Each of your aunt tillies can push or fetch
>> over ssh using the key she has in the gitadmin.gitadmin's
>> authorized_keys file.
>
> Yea. The downside to this is we have to maintain that authorized_keys
> file. Today each user can generate their own SSH key and upload to
> their own authorized_keys file.
>
> I've had enough cases of users losing their SSH key and needing to
> recreate it that I'd rather not have to manage a 50 user long
> authorized_keys file.
For what it's worth, if you haven't seen gitosis yet, you might want to
take a look - at least it makes managing the keys easy.
http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way
has a nice tutorial.
-- Asheesh.
--
He who despairs over an event is a coward, but he who holds hopes for
the human condition is a fool.
-- Albert Camus
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC] Secure central repositories by UNIX socket authentication
2008-01-28 14:23 ` Asheesh Laroia
@ 2008-01-29 3:11 ` Shawn O. Pearce
0 siblings, 0 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2008-01-29 3:11 UTC (permalink / raw)
To: Asheesh Laroia; +Cc: Junio C Hamano, git
Asheesh Laroia <asheesh@asheesh.org> wrote:
> On Mon, 28 Jan 2008, Shawn O. Pearce wrote:
> >
> >I've had enough cases of users losing their SSH key and needing to
> >recreate it that I'd rather not have to manage a 50 user long
> >authorized_keys file.
>
> For what it's worth, if you haven't seen gitosis yet, you might want to
> take a look - at least it makes managing the keys easy.
> http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way
> has a nice tutorial.
Yea, I've looked at it before. There's a few reasons I don't
use gitosis, although it does look to be an excellent chunk of
Git automation:
* Its access controls aren't as powerful
Frankly the contrib/hooks/update-paranoid script is a lot more
powerful then gitosis is, in terms of how it controls what
branches a user can modify, and even what files they can change
on a particular branch. And yes, I really do have rulesets that
bend that hook to its limits.
* It uses the OpenSSH authorized_keys file format
I'm required to use the F-Secure SSH commerical server at
day-job, because its "more trusthworthy" than the portable OpenSSH
distribution. It uses a different syntax for the authorized keys,
but can do essentially the same restricted command concept.
* If its in git, I prefer raw repository access
gitosis yanks stuff out into normal files to access it at runtime,
e.g. its configuration file. I've had bad experiences with CVS not
properly updating its admin files when changes are made to them.
The update-paranoid hook I use actually cats the objects right
out of the admin ODB on demand, ensuring its always evaluating
the most recent version of the access rules.
* Its Python based.
I don't grok Python, and would rather not learn to. So hacking
on gitosis isn't something that I would be doing. Ditto with
all of my day-job cohorts. We use Perl, Bourne shell, and Java,
with some tiny amount of Tk thrown about (though I'd say I'm
probably the only one there that even remotely groks Tcl/Tk).
But thanks for the pointer.
Now if others corrected all of the above in gitosis (except the
last item of course, I don't expect it to be rewritten in one of
my preferred languages) I'd reconsider using it, because inventing
wheels sucks.
--
Shawn.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-01-29 3:11 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-27 10:39 [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
2008-01-27 14:04 ` Johannes Schindelin
2008-01-27 17:32 ` Shawn O. Pearce
2008-01-27 18:51 ` Johannes Schindelin
2008-01-28 0:54 ` Shawn O. Pearce
2008-01-28 8:14 ` Dmitry Potapov
2008-01-27 22:56 ` Junio C Hamano
2008-01-28 0:16 ` git-daemon is insecure? (was: [RFC] Secure central repositories) Shawn O. Pearce
2008-01-28 3:00 ` git-daemon is insecure? Junio C Hamano
2008-01-28 3:20 ` Shawn O. Pearce
2008-01-28 0:47 ` [RFC] Secure central repositories by UNIX socket authentication Shawn O. Pearce
2008-01-28 7:25 ` Junio C Hamano
2008-01-28 7:51 ` Shawn O. Pearce
2008-01-28 14:23 ` Asheesh Laroia
2008-01-29 3:11 ` Shawn O. Pearce
2008-01-28 7:56 ` Shawn O. Pearce
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).