commit 1d26dbd4b176e993c088a1b058223d613ef9365c from: Stefan Sperling date: Thu Jun 26 15:37:42 2025 UTC add global repository access rules to gotsysd.conf This feature can be used by server administrators to override or augment access permissions set in gotsys.conf if needed. commit - c343b6811380169034b3341781bac5f3a347e7f6 commit + 1d26dbd4b176e993c088a1b058223d613ef9365c blob - 8628a270bcaa8929d1e004abd3806a8d7a407f26 blob + 59f265132b4c3bb5c9a65a0446dcdab1fd40b67d --- gotsysd/gotsysd.c +++ gotsysd/gotsysd.c @@ -1529,6 +1529,8 @@ gotsysd_shutdown(void) if (gotsysd.db_commit_fd != -1) close(gotsysd.db_commit_fd); + + free(gotsysd.global_repo_access_rules); log_info("terminating"); exit(0); @@ -1930,7 +1932,8 @@ main(int argc, char **argv) #endif apply_unveil_none(); - sysconf_main(title, gotsysd.uid_start, gotsysd.uid_end); + sysconf_main(title, gotsysd.uid_start, gotsysd.uid_end, + gotsysd.global_repo_access_rules); /* NOTREACHED */ break; default: blob - f20dbdab9b039423e97af70ac7b8e753ea7b842d blob + 9ff79bd55e851c72c79fb7789521958b3eb86210 --- gotsysd/gotsysd.conf.5 +++ gotsysd/gotsysd.conf.5 @@ -91,6 +91,76 @@ user. If not specified, the path .Pa /git will be used. +.It Ic repository Ic deny Ar identity +Deny repository access to users with the username +.Ar identity . +.Pp +Access rules set in +.Nm +apply to all repositories and override conflicting per-repository access +rules specified in +.Xr gotsys.conf 5 . +.Pp +Group names may be matched by prepending a colon +.Pq Sq \&: +to +.Ar identity . +.Pp +The special user +.Ar identity +.Dq * +(an asterisk) can be used to match all users, including the +.Dq anonymous +user. +.Pp +Multiple access rules can be specified, and the last matching rule +determines the action taken. +If no rule matches, the per-repository rules specified in +.Xr gotsys.conf 5 +will take effect. +.It Ic repository Ic permit Ar mode Ar identity +Permit repository access to users with the username +.Ar identity . +.Pp +Access rules set in +.Nm +apply to all repositories and override conflicting per-repository access +rules specified in +.Xr gotsys.conf 5 . +.Pp +The +.Ar mode +argument must be set to either +.Ic ro +for read-only access, +or +.Ic rw +for read-write access. +Group names may be matched by prepending a colon +.Pq Sq \&: +to +.Ar identity . +.Pp +The special user +.Ar identity +.Dq anonymous +can be used when public read-only access to repositories over SSH is desired. +The anonymous user has an empty password, cannot use an SSH public key, and +can only be granted read-only access. +.Pp +The special user +.Ar identity +.Dq * +(an asterisk) can be used to match all users, except the +.Dq anonymous +user. +Read-only anonymous access must be enabled explicitly. +.Pp +Multiple access rules can be specified, and the last matching rule +determines the action taken. +If no rule matches, the per-repository rules specified in +.Xr gotsys.conf 5 +will take effect. .It Ic uid range Ar start Ar end Set the start and end (inclusive) of the range from which .Xr gotsysd 8 @@ -129,6 +199,29 @@ listen on "/var/run/gotsysd.sock" repository directory "/git" uid range 5000 5999 .Ed +.Pp +Regardless of what +.Xr gotsys.conf 5 +says, allow the user account +.Dq backup-user +to read any repository: +.Bd -literal -offset indent +repository permit ro backup-user +.Ed +.Pp +Regardless of what +.Xr gotsys.conf 5 +says, make all repositories read-only: +.Bd -literal -offset indent +repository permit ro "*" +.Ed +.Pp +Regardless of what +.Xr gotsys.conf 5 +says, make all repositories inaccessible: +.Bd -literal -offset indent +repository deny "*" +.Ed .Sh SEE ALSO .Xr got 1 , .Xr gotd 8 , blob - f72e5f0ff065e99ca9f7e168dbd69ae18ba02940 blob + b6a2d93351ce9620926282a1ad82b34e21529cc6 --- gotsysd/gotsysd.h +++ gotsysd/gotsysd.h @@ -112,6 +112,7 @@ struct gotsysd { int sysconf_fd; char *sysconf_commit_id_str; struct gotsysd_access_rule_list access_rules; + struct gotsys_access_rule_list *global_repo_access_rules; struct event sysconf_tmo; struct gotsysd_pending_sysconf_cmd_list sysconf_pending; @@ -178,6 +179,8 @@ enum gotsysd_imsg_type { GOTSYSD_IMSG_SYSCONF_AUTHORIZED_KEYS_DONE, GOTSYSD_IMSG_SYSCONF_REPO, GOTSYSD_IMSG_SYSCONF_REPOS_DONE, + GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULE, + GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULES_DONE, GOTSYSD_IMSG_SYSCONF_ACCESS_RULE, GOTSYSD_IMSG_SYSCONF_ACCESS_RULES_DONE, GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES, @@ -555,6 +558,8 @@ const struct got_error *gotsys_imsg_recv_authorized_ke struct imsg *); const struct got_error *gotsys_imsg_recv_authorized_keys(struct imsg *, struct gotsys_authorized_keys_list *); +const struct got_error *gotsys_imsg_send_access_rule(struct gotsysd_imsgev *, + struct gotsys_access_rule *, int); const struct got_error *gotsys_imsg_send_repositories(struct gotsysd_imsgev *, struct gotsys_repolist *); const struct got_error *gotsys_imsg_recv_repository(struct gotsys_repo **, blob - a9f801243fe0f3232af577fb8cdcfc0cadbb2442 blob + 7e3a2d02aa64a6ad196c47fb3ce747ed6294522b --- gotsysd/helpers.c +++ gotsysd/helpers.c @@ -1015,6 +1015,8 @@ helpers_dispatch_sysconf(int fd, short event, void *ar case GOTSYSD_IMSG_SYSCONF_WRITE_CONF_GROUP_MEMBERS_DONE: case GOTSYSD_IMSG_SYSCONF_WRITE_CONF_GROUPS_DONE: case GOTSYSD_IMSG_SYSCONF_REPO: + case GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULE: + case GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULES_DONE: case GOTSYSD_IMSG_SYSCONF_ACCESS_RULE: case GOTSYSD_IMSG_SYSCONF_ACCESS_RULES_DONE: case GOTSYSD_IMSG_SYSCONF_PROTECTED_TAG_NAMESPACES: blob - be28e070e622a6bb2ad28de4120884dfffc27dfc blob + 7996125cee248ac4fb8218d775a7a8b6a3f47ddf --- gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c +++ gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c @@ -49,6 +49,7 @@ static size_t nprotected_refs_needed; static size_t nprotected_refs_received; static int gotd_conf_tmpfd = -1; static char *gotd_conf_tmppath; +static struct gotsys_access_rule_list global_repo_access_rules; enum writeconf_state { WRITECONF_STATE_EXPECT_USERS, @@ -96,13 +97,88 @@ send_done(struct gotsysd_imsgev *iev) } static const struct got_error * +write_access_rule(const char *access, const char * authorization, + const char *identifier) +{ + int ret; + + ret = dprintf(gotd_conf_tmpfd, "\t%s%s%s\n", + access, authorization, identifier); + if (ret == -1) + return got_error_from_errno2("dprintf", gotd_conf_tmppath); + if (ret != 1 + strlen(access) + strlen(authorization) + + strlen(identifier) + 1) { + return got_error_fmt(GOT_ERR_IO, + "short write to %s", gotd_conf_tmppath); + } + + return NULL; +} + +static const struct got_error * +write_global_access_rules(void) +{ + const struct got_error *err; + struct gotsys_access_rule *rule; + + STAILQ_FOREACH(rule, &global_repo_access_rules, entry) { + const char *access, *authorization; + + switch (rule->access) { + case GOTSYS_ACCESS_DENIED: + access = "deny "; + break; + case GOTSYS_ACCESS_PERMITTED: + access = "permit "; + break; + default: + return got_error_fmt(GOT_ERR_PARSE_CONFIG, + "access rule with unknown access flag %d", + rule->access); + } + + if (rule->authorization & GOTSYS_AUTH_WRITE) + authorization = "rw "; + else if (rule->authorization & GOTSYS_AUTH_READ) + authorization = "ro "; + else + authorization = ""; + + if (strcmp(rule->identifier, "*") == 0) { + struct gotsys_user *user; + + STAILQ_FOREACH(user, &gotsysconf.users, entry) { + /* + * Anonymous read access must be enabled + * explicitly, not via *. + */ + if (rule->access == GOTSYS_ACCESS_PERMITTED && + strcmp(user->name, "anonymous") == 0) + continue; + err = write_access_rule(access, authorization, + user->name); + if (err) + return err; + } + } else { + err = write_access_rule(access, authorization, + rule->identifier); + if (err) + return err; + } + } + + return NULL; +} + +static const struct got_error * write_access_rules(struct gotsys_access_rule_list *rules) { + const struct got_error *err; struct gotsys_access_rule *rule; STAILQ_FOREACH(rule, rules, entry) { const char *access, *authorization; - int ret; switch (rule->access) { case GOTSYS_ACCESS_DENIED: @@ -124,16 +200,10 @@ write_access_rules(struct gotsys_access_rule_list *rul else authorization = ""; - ret = dprintf(gotd_conf_tmpfd, "\t%s%s%s\n", - access, authorization, rule->identifier); - if (ret == -1) - return got_error_from_errno2("dprintf", - gotd_conf_tmppath); - if (ret != 1 + strlen(access) + strlen(authorization) + - strlen(rule->identifier) + 1) { - return got_error_fmt(GOT_ERR_IO, - "short write to %s", gotd_conf_tmppath); - } + err = write_access_rule(access, authorization, + rule->identifier); + if (err) + return err; } return NULL; @@ -326,6 +396,10 @@ write_gotd_conf(void) if (err) return err; + err = write_global_access_rules(); + if (err) + return err; + err = write_protected_refs(repo); if (err) return err; @@ -479,8 +553,34 @@ dispatch_event(int fd, short event, void *arg) break; TAILQ_INSERT_TAIL(&gotsysconf.repos, repo, entry); repo_cur = repo; + break; + } + case GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULE: { + struct gotsys_access_rule *rule; + if (writeconf_state != WRITECONF_STATE_EXPECT_REPOS) { + err = got_error_fmt(GOT_ERR_PRIVSEP_MSG, + "received unexpected imsg %d while in " + "state %d\n", imsg.hdr.type, + writeconf_state); + break; + } + err = gotsys_imsg_recv_access_rule(&rule, &imsg, + NULL, NULL); + if (err) + break; + STAILQ_INSERT_TAIL(&global_repo_access_rules, rule, + entry); break; } + case GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULES_DONE: + if (writeconf_state != WRITECONF_STATE_EXPECT_REPOS) { + err = got_error_fmt(GOT_ERR_PRIVSEP_MSG, + "received unexpected imsg %d while in " + "state %d\n", imsg.hdr.type, + writeconf_state); + break; + } + break; case GOTSYSD_IMSG_SYSCONF_ACCESS_RULE: { struct gotsys_access_rule_list *rules; struct gotsys_access_rule *rule; @@ -647,6 +747,7 @@ main(int argc, char *argv[]) while (!attached) sleep(1); #endif + STAILQ_INIT(&global_repo_access_rules); gotsys_conf_init(&gotsysconf); event_init(); blob - 5382a6cf8d17e10cec59c8629b221a1dbf6cf6d2 blob + 845732b3050151e7063b1800afbf3857993212b6 --- gotsysd/parse.y +++ gotsysd/parse.y @@ -50,6 +50,7 @@ #include "log.h" #include "gotsysd.h" +#include "gotsys.h" #ifndef GOTD_USER #define GOTD_USER "_gotd" @@ -95,6 +96,8 @@ static struct gotsysd *gotsysd; static enum gotsysd_procid gotsysd_proc_id; static void conf_new_access_rule(enum gotsysd_access, char *); +static const struct got_error *conf_new_repo_access_rule(enum gotsys_access, + int, const char *); typedef struct { union { @@ -108,6 +111,7 @@ typedef struct { %} %token ERROR LISTEN ON USER GOTD DIRECTORY REPOSITORY UID RANGE PERMIT +%token DENY RO RW %token STRING %token NUMBER @@ -229,7 +233,43 @@ main : LISTEN ON STRING { $2); } else free($2); + } + | REPOSITORY DENY numberstring { + const struct got_error *err; + + err = conf_new_repo_access_rule(GOTSYS_ACCESS_DENIED, + 0, $3); + if (err) { + yyerror("%s", err->msg); + free($3); + YYERROR; + } + free($3); + } + | REPOSITORY PERMIT RO numberstring { + const struct got_error *err; + + err = conf_new_repo_access_rule(GOTSYS_ACCESS_PERMITTED, + GOTSYS_AUTH_READ, $4); + if (err) { + yyerror("%s", err->msg); + free($4); + YYERROR; + } + free($4); } + | REPOSITORY PERMIT RW numberstring { + const struct got_error *err; + + err = conf_new_repo_access_rule(GOTSYS_ACCESS_PERMITTED, + GOTSYS_AUTH_READ | GOTSYS_AUTH_WRITE, $4); + if (err) { + yyerror("%s", err->msg); + free($4); + YYERROR; + } + free($4); + } ; %% @@ -266,6 +306,7 @@ lookup(char *s) { /* This has to be sorted always. */ static const struct keywords keywords[] = { + { "deny", DENY }, { "directory", DIRECTORY }, { "gotd", GOTD }, { "listen", LISTEN }, @@ -273,6 +314,8 @@ lookup(char *s) { "permit", PERMIT }, { "range", RANGE }, { "repository", REPOSITORY }, + { "ro", RO }, + { "rw", RW }, { "uid", UID }, { "user", USER }, }; @@ -577,6 +620,7 @@ gotsysd_parse_config(const char *filename, enum gotsys { struct sym *sym, *next; int require_config_file = 0; + struct gotsys_access_rule_list *global_repo_access_rules; memset(pgotsysd, 0, sizeof(*pgotsysd)); @@ -584,6 +628,16 @@ gotsysd_parse_config(const char *filename, enum gotsys gotsysd_proc_id = proc_id; STAILQ_INIT(&gotsysd->access_rules); + + global_repo_access_rules = malloc(sizeof(*global_repo_access_rules)); + if (global_repo_access_rules == NULL) { + fprintf(stderr, "%s: malloc: %s\n", __func__, + strerror(errno)); + return -1; + } + gotsysd->global_repo_access_rules = global_repo_access_rules; + STAILQ_INIT(gotsysd->global_repo_access_rules); + global_repo_access_rules = NULL; /* Apply default values. */ if (strlcpy(gotsysd->unix_socket_path, GOTSYSD_UNIX_SOCKET, @@ -740,3 +794,33 @@ conf_new_access_rule(enum gotsysd_access access, char STAILQ_INSERT_TAIL(&gotsysd->access_rules, rule, entry); } + +static const struct got_error * +conf_new_repo_access_rule(enum gotsys_access access, int authorization, + const char *identifier) +{ + const struct got_error *err; + struct gotsys_access_rule *rule; + const char *name = identifier; + + if (strcmp(name, "*") != 0) { + if (name[0] == ':') { + name++; + err = gotsys_conf_validate_name(name, "group"); + if (err) + return err; + } else { + err = gotsys_conf_validate_name(name, "user"); + if (err) + return err; + } + } + + err = gotsys_conf_new_access_rule(&rule, access, authorization, + identifier, NULL, NULL); + if (err) + return err; + + STAILQ_INSERT_TAIL(gotsysd->global_repo_access_rules, rule, entry); + return NULL; +} blob - e3f8794ab608fefd10b1564764fb55cb242295fa blob + 7fee8a40df4446794f8d3b8d9b88ebcfe89559f4 --- gotsysd/sysconf.c +++ gotsysd/sysconf.c @@ -79,6 +79,7 @@ static struct gotsysd_sysconf { size_t *nprotected_refs_cur; size_t nprotected_refs_needed; size_t nprotected_refs_received; + struct gotsys_access_rule_list *global_repo_access_rules; } gotsysd_sysconf; static struct gotsys_conf gotsysconf; @@ -729,6 +730,7 @@ static const struct got_error * send_gotsysconf(struct gotsysd_imsgev *iev) { const struct got_error *err; + struct gotsys_access_rule *rule; err = gotsys_imsg_send_users(iev, &gotsysconf.users, GOTSYSD_IMSG_SYSCONF_WRITE_CONF_USERS, @@ -743,6 +745,18 @@ send_gotsysconf(struct gotsysd_imsgev *iev) GOTSYSD_IMSG_SYSCONF_WRITE_CONF_GROUPS_DONE); if (err) return err; + + STAILQ_FOREACH(rule, gotsysd_sysconf.global_repo_access_rules, entry) { + err = gotsys_imsg_send_access_rule(iev, rule, + GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULE); + if (err) + return err; + } + + if (gotsysd_imsg_compose_event(iev, + GOTSYSD_IMSG_SYSCONF_GLOBAL_ACCESS_RULES_DONE, 0, -1, + NULL, 0) == -1) + return got_error_from_errno("gotsysd_imsg_compose_event"); err = gotsys_imsg_send_repositories(iev, &gotsysconf.repos); if (err) @@ -1234,7 +1248,8 @@ sysconf_dispatch(int fd, short event, void *arg) } void -sysconf_main(const char *title, uid_t uid_start, uid_t uid_end) +sysconf_main(const char *title, uid_t uid_start, uid_t uid_end, + struct gotsys_access_rule_list *global_repo_access_rules) { struct event evsigint, evsigterm, evsighup, evsigusr1; struct gotsysd_imsgev *iev = &gotsysd_sysconf.parent_iev; @@ -1249,6 +1264,7 @@ sysconf_main(const char *title, uid_t uid_start, uid_t gotsysd_sysconf.state = SYSCONF_STATE_EXPECT_PARSING_SUCCESS; gotsysd_sysconf.uid_start = uid_start; gotsysd_sysconf.uid_end = uid_end; + gotsysd_sysconf.global_repo_access_rules = global_repo_access_rules; signal_set(&evsigint, SIGINT, sysconf_sighdlr, NULL); signal_set(&evsigterm, SIGTERM, sysconf_sighdlr, NULL); blob - 69dabe3a4134e1f341dc15b645eb8fbd6f495b93 blob + 8391a0256347fa9315b8a27c887e89719ade65f5 --- gotsysd/sysconf.h +++ gotsysd/sysconf.h @@ -14,4 +14,4 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -void sysconf_main(const char *, uid_t, uid_t); +void sysconf_main(const char *, uid_t, uid_t, struct gotsys_access_rule_list *); blob - fa11004e9e14a9712ef0b3c3ea8e2c4dfc98123a blob + 05fad5e984044a6e291365ac1358ec288a6da1c0 --- lib/gotsys_conf.c +++ lib/gotsys_conf.c @@ -726,14 +726,16 @@ gotsys_conf_new_access_rule(struct gotsys_access_rule return got_error_fmt(GOT_ERR_PARSE_CONFIG, "empty group name in access rule"); - STAILQ_FOREACH(group, groups, entry) { - if (strcmp(group->name, name) == 0) - break; - } - if (group == NULL) { - return got_error_fmt(GOT_ERR_PARSE_CONFIG, - "reference to undeclared group '%s' via " - "access rule", name); + if (groups) { + STAILQ_FOREACH(group, groups, entry) { + if (strcmp(group->name, name) == 0) + break; + } + if (group == NULL) { + return got_error_fmt(GOT_ERR_PARSE_CONFIG, + "reference to undeclared group '%s' via " + "access rule", name); + } } } else if (strcmp(name, "anonymous") == 0) { if (access == GOTSYS_ACCESS_PERMITTED && @@ -742,7 +744,7 @@ gotsys_conf_new_access_rule(struct gotsys_access_rule "the \"anonymous\" user must not have write " "permission"); } - } else { + } else if (users) { struct gotsys_user *user = NULL; STAILQ_FOREACH(user, users, entry) { blob - 7d5a4aa34f40e6f7de34db562bf6b6c32e22df98 blob + be6c5e0e7bf717619f74605bb70de1188f08480e --- lib/gotsys_imsg.c +++ lib/gotsys_imsg.c @@ -616,9 +616,9 @@ done: return err; } -static const struct got_error * -send_access_rule(struct gotsysd_imsgev *iev, - struct gotsys_access_rule *rule) +const struct got_error * +gotsys_imsg_send_access_rule(struct gotsysd_imsgev *iev, + struct gotsys_access_rule *rule, int imsg_type) { struct gotsysd_imsg_sysconf_access_rule irule; struct ibuf *wbuf = NULL; @@ -637,7 +637,7 @@ send_access_rule(struct gotsysd_imsgev *iev, irule.authorization = rule->authorization; irule.identifier_len = strlen(rule->identifier); - wbuf = imsg_create(&iev->ibuf, GOTSYSD_IMSG_SYSCONF_ACCESS_RULE, + wbuf = imsg_create(&iev->ibuf, imsg_type, 0, 0, sizeof(irule) + irule.identifier_len); if (wbuf == NULL) return got_error_from_errno("imsg_create SYSCONF_ACCESS_RULE"); @@ -776,7 +776,8 @@ send_repo(struct gotsysd_imsgev *iev, struct gotsys_re imsg_close(&iev->ibuf, wbuf); STAILQ_FOREACH(rule, &repo->access_rules, entry) { - err = send_access_rule(iev, rule); + err = gotsys_imsg_send_access_rule(iev, rule, + GOTSYSD_IMSG_SYSCONF_ACCESS_RULE); if (err) return err; } blob - 7e60f16b3db43adbee999e9d75a456e2c5327ea1 blob + cd4dfe61444049bb6012ccddeb973b174dbe2083 --- regress/gotsysd/test_gotsysd.sh +++ regress/gotsysd/test_gotsysd.sh @@ -1522,6 +1522,107 @@ EOF fi echo "gotsh: foo: Permission denied" > $testroot/stderr.expected + grep '^gotsh:' $testroot/stderr > $testroot/stderr.filtered + cmp -s $testroot/stderr.expected $testroot/stderr.filtered + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stderr.expected $testroot/stderr.filtered + test_done "$testroot" "$ret" + return 1 + fi + + test_done "$testroot" "$ret" +} + +test_override_access_rules() { + local testroot=`test_init override_access_rules 1` + + # Override gotsys.conf access rules which deny access to foo.git. + echo "repository permit ro ${GOTSYSD_DEV_USER}" | \ + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} \ + 'cat >> /etc/gotsysd.conf' + + # Restart gotsysd (XXX need a better way to do this...) + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} 'pkill -xf /usr/local/sbin/gotsysd' + sleep 1 + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} '/usr/local/sbin/gotsysd -vvv' + sleep 1 + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} 'gotsys apply -w' > /dev/null + + # Cloning repository foo should now succeed. + got clone -q -i ${GOTSYSD_SSH_KEY} -b foo \ + ${GOTSYSD_DEV_USER}@${VMIP}:foo.git $testroot/foo.git \ + > $testroot/stdout 2> $testroot/stderr + ret=$? + if [ $ret -ne 0 ]; then + echo "got clone failed unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + + # gotsys.git access should now be read-only. + got clone -q -i ${GOTSYSD_SSH_KEY} \ + ${GOTSYSD_DEV_USER}@${VMIP}:gotsys.git $testroot/gotsys2.git \ + > $testroot/stdout 2> $testroot/stderr + ret=$? + if [ $ret -ne 0 ]; then + echo "got clone failed unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + got checkout -q $testroot/gotsys2.git $testroot/wt >/dev/null + ret=$? + if [ $ret -ne 0 ]; then + echo "got checkout failed unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + + crypted_vm_pw=`echo ${GOTSYSD_VM_PASSWORD} | encrypt | tr -d '\n'` + crypted_pw=`echo ${GOTSYSD_DEV_PASSWORD} | encrypt | tr -d '\n'` + sshkey=`cat ${GOTSYSD_SSH_PUBKEY}` + cat > ${testroot}/wt/gotsys.conf </dev/null) + local commit_id=`git_show_head $testroot/gotsys2.git` + + got send -q -i ${GOTSYSD_SSH_KEY} -r ${testroot}/gotsys2.git \ + > $testroot/stdout 2> $testroot/stderr + ret=$? + if [ $ret -eq 0 ]; then + echo "got send succeeded unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + + echo "gotsh: gotsys.git: Permission denied" > $testroot/stderr.expected grep '^gotsh:' $testroot/stderr > $testroot/stderr.filtered cmp -s $testroot/stderr.expected $testroot/stderr.filtered ret=$? @@ -1530,7 +1631,74 @@ EOF test_done "$testroot" "$ret" return 1 fi + test_done "$testroot" "$ret" +} +test_override_all_user_access() { + local testroot=`test_init override_all_user_access 1` + + # Override gotsys.conf access rules which deny access to foo.git. + echo 'repository deny "*"' | \ + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} \ + 'cat >> /etc/gotsysd.conf' + + # Restart gotsysd (XXX need a better way to do this...) + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} 'pkill -xf /usr/local/sbin/gotsysd' + sleep 1 + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} '/usr/local/sbin/gotsysd -vvv' + sleep 1 + ssh -q -i ${GOTSYSD_SSH_KEY} root@${VMIP} 'gotsys apply -w' > /dev/null + + # Cloning any repository as any user should now fail. + for user in ${GOTSYSD_TEST_USER} ${GOTSYSD_DEV_USER} anonymous; do + got clone -q -i ${GOTSYSD_SSH_KEY} -b foo \ + ${user}@${VMIP}:foo.git $testroot/foo-${user}.git \ + > $testroot/stdout 2> $testroot/stderr + ret=$? + if [ $ret -eq 0 ]; then + echo "got clone succeeded unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + + echo "got-fetch-pack: foo: Permission denied" \ + > $testroot/stderr.expected + grep '^got-fetch-pack:' $testroot/stderr \ + > $testroot/stderr.filtered + cmp -s $testroot/stderr.expected $testroot/stderr.filtered + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stderr.expected \ + $testroot/stderr.filtered + test_done "$testroot" "$ret" + return 1 + fi + + got clone -q -i ${GOTSYSD_SSH_KEY} \ + ${user}@${VMIP}:gotsys.git \ + $testroot/gotsys-${user}.git \ + > $testroot/stdout 2> $testroot/stderr + ret=$? + if [ $ret -eq 0 ]; then + echo "got clone succeeded unexpectedly" >&2 + test_done "$testroot" 1 + return 1 + fi + + echo "got-fetch-pack: gotsys.git: Permission denied" \ + > $testroot/stderr.expected + grep '^got-fetch-pack:' $testroot/stderr \ + > $testroot/stderr.filtered + cmp -s $testroot/stderr.expected $testroot/stderr.filtered + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stderr.expected \ + $testroot/stderr.filtered + test_done "$testroot" "$ret" + return 1 + fi + done + test_done "$testroot" "$ret" } @@ -1546,3 +1714,5 @@ run_test test_bad_gotsysconf run_test test_set_head run_test test_protect_refs run_test test_deny_access +run_test test_override_access_rules +run_test test_override_all_user_access