commit - 6344415e75626d992b2fa13cc510c74d0cec80b5
commit + 25bbf24a4442db753e05bed1d01ecded81615376
blob - 6f7bb8b9d454761d0ff210092f43b109d4c69127
blob + c2cc691b9bb15f564d17e48bfb3b36ff76d9c615
--- gotwebd/config.c
+++ gotwebd/config.c
TAILQ_INIT(&env->servers);
TAILQ_INIT(&env->sockets);
TAILQ_INIT(&env->addresses);
+ STAILQ_INIT(&env->access_rules);
for (i = 0; i < PRIV_FDS__MAX; i++)
env->priv_fd[i] = -1;
blob - 4fabc5af7e3f1c8300538ca5ea44204867738233
blob + f5f12fac0038ec242e8a128d63ef32e2709c7062
--- gotwebd/gotwebd.conf.5
+++ gotwebd/gotwebd.conf.5
effectively disables chroot.
.It Ic disable authentication
Disable authentication, allowing any browser to view any repository.
+This setting can be overridden on a per-server or per-repository basis.
.It Ic enable authentication Oo Ic insecure Oc
Enable authentication, requiring browsers to present a login token cookie
-before read-only repositories access is granted.
+before read-only repository access is granted.
+This setting can be overridden on a per-server or per-repository basis.
.Pp
Browsers presenting a valid login token cookie will be mapped to the
user account which obtained the login token over SSH from the
command of
.Xr gotsh 1 .
.Pp
-Browsers with a missing or invalid cookie will be mapped to the user
-account which runs
+Unauthenticated browsers will be mapped to the user account which runs
.Xr httpd 8 .
This user account can be set with the
.Ic www user
directive.
-Permitting this user to read a repository allows authentication to be
-bypassed for this particular repository.
+Attempts to read repositories as this user will be denied unless
+authentication is disabled for the repository.
.Pp
Unless the
.Ic insecure
.Pp
This path must be valid in the web server's URL space since browsers
will attempt to fetch it.
+.It Ic disable authentication
+Disable authentication for this server, allowing any browser to view any repository.
+This setting can be overridden on a per-repository basis.
+.Pp
+If not specified, the global configuration context determines
+whether authentication is disabled.
+.It Ic enable authentication
+Enable authentication, requiring browsers to present a login token cookie
+before read-only repository access is granted.
+This setting can be overridden on a per-repository basis.
+.Pp
+If not specified, the global configuration context determines
+whether authentication is enabled.
.It Ic logo_url Ar url
Set a hyperlink for the logo.
Defaults to
.Cm chroot
directive must be used before the server declaration in order to
take effect.
+.It Ic repository Ar name Brq ...
+Set options which apply to a particular repository served by this server.
+.Pp
+A repository context is declared with a unique
+.Ar name ,
+followed by repository-specific configuration directives inside curly braces.
+The repository
+.Ar name
+is looked up within the
+.Ar repos_path ,
+where it should exist with or without a
+.Dq .git
+suffix.
+If a repository does not exist then the options set in its context will
+be ignored.
+.Pp
+For each repository, access rules can be configured using the
+.Ic permit
+and
+.Ic deny
+configuration directives.
+Multiple access rules can be specified, and the last matching rule
+determines the action taken.
+If no rule matches and authentication is enabled,
+.Xr gotwebd 8
+will behave as if the repository did not exist to avoid revealing
+the existence of secret repositories to unauthorized users.
+.Pp
+The available repository configuration directives are as follows:
+.Bl -tag -width Ds
+.It Ic deny Ar identity
+Deny repository access to users with the username
+.Ar identity .
+Group names may be matched by prepending a colon
+.Pq Sq \&:
+to
+.Ar identity .
+Numeric IDs are also accepted.
+.Pp
+When a
+.Ic deny
+rule matches an error page will be sent back to the browser, revealing the
+existence of the requested repository.
+.It Ic permit Ar identity
+Permit repository access to users with the username
+.Ar identity .
+Group names may be matched by prepending a colon
+.Pq Sq \&:
+to
+.Ar identity .
+Numeric IDs are also accepted.
+.It Ic disable authentication
+Disable authentication, allowing any browser to view the repository.
+Any access rules configured with
+.Ic permit
+or
+.Ic deny
+directives for this repository will be ignored.
+.Pp
+If not specified, the server context or global context determines
+whether authentication is disabled.
+.It Ic enable authentication
+Enable authentication, requiring browsers to present a login token cookie
+before read-only repository access is granted.
+.Pp
+If not specified, the server context or global context determines
+whether authentication is enabled.
+.El
.It Ic respect_exportok Ar on | off
Set whether to display the repository only if it contains the magic
.Pa git-daemon-export-ok
listening socket.
.El
.Sh EXAMPLES
-A sample configuration:
+A sample configuration which allows public browsing:
.Bd -literal -offset indent
www user "www" # www username needs quotes since www is a keyword
site_name "my public repos"
site_owner "Flan Hacker"
site_link "Flan' Projects"
+ disable authentication
}
.Ed
.Pp
server "localhost" {
site_name "my public repos"
repos_path "/var/git"
+ disable authentication
}
.Ed
+.Pp
+The following example illustrates the use of directives related to
+authentication:
+.Bd -literal -offset indent
+# 3 scopes: global, per-server, per-repository
+
+disable authentication # override the default which is 'enable'
+
+# Allow user "admin" to read anything unless overridden with a
+# "deny" rule later.
+permit "admin"
+
+server "public" {
+ # inherit global default, i.e. authentication is disabled
+ repos_path "/var/www/got/public"
+}
+
+server "secure" {
+ enable authentication # override global default
+
+ permit flan_squee # grant access to flan_squee
+ permit :developers # grant access to developers group
+
+ repos_path "/var/git"
+
+ repository "got" { # /var/git/got and /var/git/got.git
+ # Grant access to users who have authenticated as
+ # the anonymous user to gotsh(1), which anyone with
+ # an SSH client sbould be able to do.
+ # Dumb web crawlers will remain locked out.
+ permit anonymous
+ }
+
+ repository "public" {
+ # As an exception, allow any web browsers and
+ # web crawlers to view this repository.
+ disable authentication
+ }
+
+ repository "secret" {
+ deny admin # not even the admin can read this
+ }
+}
+.Ed
.Sh SEE ALSO
.Xr got 1 ,
.Xr httpd.conf 5 ,
blob - d93b9521294f410e5d6c81e702a484e4d973280b
blob + 8169c11f1d7782271f727508f61407829f6f307b
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
char ifname[IFNAMSIZ];
};
TAILQ_HEAD(addresslist, address);
+
+enum gotwebd_auth_config {
+ GOTWEBD_AUTH_DISABLED = 0xf00000ff,
+ GOTWEBD_AUTH_SECURE = 0x00808000,
+ GOTWEBD_AUTH_INSECURE = 0x0f7f7f00
+};
+enum gotwebd_access {
+ GOTWEBD_ACCESS_DENIED = -1,
+ GOTWEBD_ACCESS_PERMITTED = 1
+};
+
+struct gotwebd_access_rule {
+ STAILQ_ENTRY(gotwebd_access_rule) entry;
+
+ enum gotwebd_access access;
+ char *identifier;
+};
+STAILQ_HEAD(gotwebd_access_rule_list, gotwebd_access_rule);
+
+struct gotwebd_repo {
+ TAILQ_ENTRY(gotwebd_repo) entry;
+
+ char name[NAME_MAX];
+
+ enum gotwebd_auth_config auth_config;
+ struct gotwebd_access_rule_list access_rules;
+};
+TAILQ_HEAD(gotwebd_repolist, gotwebd_repo);
+
struct server {
TAILQ_ENTRY(server) entry;
int show_repo_description;
int show_repo_cloneurl;
int respect_exportok;
+
+ enum gotwebd_auth_config auth_config;
+ struct gotwebd_access_rule_list access_rules;
+
+ struct gotwebd_repolist repos;
+ int nrepos;
};
TAILQ_HEAD(serverlist, server);
};
TAILQ_HEAD(socketlist, socket);
-enum gotwebd_auth_config {
- GOTWEBD_AUTH_DISABLED = 0xf00000ff,
- GOTWEBD_AUTH_SECURE = 0x00808000,
- GOTWEBD_AUTH_INSECURE = 0x0f7f7f00
-};
-
struct passwd;
struct gotwebd {
struct serverlist servers;
struct event auth_pause_ev;
enum gotwebd_auth_config auth_config;
+ struct gotwebd_access_rule_list access_rules;
int pack_fds[GOTWEB_PACK_NUM_TEMPFILES];
int priv_fd[PRIV_FDS__MAX];
int gotweb_render_authorized(struct template *);
/* parse.y */
+struct gotwebd_repo * gotwebd_new_repo(const char *);
int parse_config(const char *, struct gotwebd *);
int cmdline_symset(char *);
blob - a1cc8141bf5de34f6aa065a7e4ec8343012ff868
blob + 9d3b654730ec2f623f12354a7b55a35241f1ee9e
--- gotwebd/parse.y
+++ gotwebd/parse.y
int addr_dup_check(struct addresslist *, struct address *);
void add_addr(struct address *);
+static struct gotwebd_repo *new_repo;
+static struct gotwebd_repo *conf_new_repo(struct server *, const char *);
+static void conf_new_access_rule(
+ struct gotwebd_access_rule_list *,
+ enum gotwebd_access, char *);
+
typedef struct {
union {
long long number;
%token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
%token SERVER CHROOT CUSTOM_CSS SOCKET
%token SUMMARY_COMMITS_DISPLAY SUMMARY_TAGS_DISPLAY USER AUTHENTICATION
-%token ENABLE DISABLE INSECURE
+%token ENABLE DISABLE INSECURE REPOSITORY PERMIT DENY
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> boolean
%type <v.string> listen_addr
+%type <v.string> numberstring
%%
fatal("cannot store variable");
free($1);
free($3);
+ }
+ ;
+
+numberstring : STRING
+ | NUMBER {
+ if (asprintf(&$$, "%lld", (long long)$1) == -1) {
+ yyerror("asprintf: %s", strerror(errno));
+ YYERROR;
+ }
}
;
gotwebd->auth_sock = sockets_conf_new_socket(-1, h);
free($3);
}
+ | PERMIT numberstring {
+ conf_new_access_rule(&gotwebd->access_rules,
+ GOTWEBD_ACCESS_PERMITTED, $2);
+ }
+ | DENY numberstring {
+ conf_new_access_rule(&gotwebd->access_rules,
+ GOTWEBD_ACCESS_DENIED, $2);
+ }
;
server : SERVER STRING {
}
new_srv->summary_tags_display = $2;
}
+ | DISABLE AUTHENTICATION {
+ if (new_srv->auth_config != 0) {
+ yyerror("ambiguous authentication "
+ "setting for server %s",
+ new_srv->name);
+ YYERROR;
+ }
+ new_srv->auth_config = GOTWEBD_AUTH_DISABLED;
+ }
+ | ENABLE AUTHENTICATION {
+ if (new_srv->auth_config != 0) {
+ yyerror("ambiguous authentication "
+ "setting for server %s",
+ new_srv->name);
+ YYERROR;
+ }
+ new_srv->auth_config = GOTWEBD_AUTH_SECURE;
+ }
+ | ENABLE AUTHENTICATION INSECURE {
+ if (new_srv->auth_config != 0) {
+ yyerror("ambiguous authentication "
+ "setting for server %s",
+ new_srv->name);
+ YYERROR;
+ }
+ new_srv->auth_config = GOTWEBD_AUTH_INSECURE;
+ }
+ | PERMIT numberstring {
+ conf_new_access_rule(&new_srv->access_rules,
+ GOTWEBD_ACCESS_PERMITTED, $2);
+ }
+ | DENY numberstring {
+ conf_new_access_rule(&new_srv->access_rules,
+ GOTWEBD_ACCESS_DENIED, $2);
+ }
+ | repository
;
serveropts2 : serveropts2 serveropts1 nl
| serveropts1 optnl
+ ;
+
+repository : REPOSITORY STRING {
+ struct gotwebd_repo *repo;
+
+ TAILQ_FOREACH(repo, &new_srv->repos, entry) {
+ if (strcmp(repo->name, $2) == 0) {
+ yyerror("duplicate repository "
+ "'%s' in server '%s'", $2,
+ new_srv->name);
+ free($2);
+ YYERROR;
+ }
+ }
+
+ new_repo = conf_new_repo(new_srv, $2);
+ free($2);
+ } '{' optnl repoopts2 '}' {
+ }
+ ;
+
+repoopts2 : repoopts2 repoopts1 nl
+ | repoopts1 optnl
+ ;
+
+repoopts1 : DISABLE AUTHENTICATION {
+ if (new_repo->auth_config != 0) {
+ yyerror("ambiguous authentication "
+ "setting for repository %s",
+ new_repo->name);
+ YYERROR;
+ }
+ new_repo->auth_config = GOTWEBD_AUTH_DISABLED;
+ }
+ | ENABLE AUTHENTICATION {
+ if (new_repo->auth_config != 0) {
+ yyerror("ambiguous authentication "
+ "setting for repository %s",
+ new_repo->name);
+ YYERROR;
+ }
+ new_repo->auth_config = GOTWEBD_AUTH_SECURE;
+ }
+ | ENABLE AUTHENTICATION INSECURE {
+ if (new_repo->auth_config != 0) {
+ yyerror("ambiguous authentication "
+ "setting for repository %s",
+ new_repo->name);
+ YYERROR;
+ }
+ new_repo->auth_config = GOTWEBD_AUTH_INSECURE;
+ }
+ | PERMIT numberstring {
+ conf_new_access_rule(&new_repo->access_rules,
+ GOTWEBD_ACCESS_PERMITTED, $2);
+ }
+ | DENY numberstring {
+ conf_new_access_rule(&new_repo->access_rules,
+ GOTWEBD_ACCESS_DENIED, $2);
+ }
;
nl : '\n' optnl
{ "authentication", AUTHENTICATION },
{ "chroot", CHROOT },
{ "custom_css", CUSTOM_CSS },
+ { "deny", DENY },
{ "disable", DISABLE },
{ "enable", ENABLE },
{ "insecure", INSECURE },
{ "max_commits_display", MAX_COMMITS_DISPLAY },
{ "max_repos_display", MAX_REPOS_DISPLAY },
{ "on", ON },
+ { "permit", PERMIT },
{ "port", PORT },
{ "prefork", PREFORK },
{ "repos_path", REPOS_PATH },
+ { "repository", REPOSITORY },
{ "respect_exportok", RESPECT_EXPORTOK },
{ "server", SERVER },
{ "show_repo_age", SHOW_REPO_AGE },
parse_config(const char *filename, struct gotwebd *env)
{
struct sym *sym, *next;
+ struct server *srv;
+ struct gotwebd_repo *repo;
if (config_init(env) == -1)
fatalx("failed to initialize configuration");
env->auth_config = GOTWEBD_AUTH_SECURE;
break;
}
+ TAILQ_FOREACH(srv, &env->servers, entry) {
+ if (srv->auth_config == 0)
+ srv->auth_config = env->auth_config;
+ TAILQ_FOREACH(repo, &srv->repos, entry) {
+ if (repo->auth_config == 0)
+ repo->auth_config = srv->auth_config;
+ }
+ }
return (0);
}
srv->max_commits_display = D_MAXCOMMITDISP;
srv->summary_commits_display = D_MAXSLCOMMDISP;
srv->summary_tags_display = D_MAXSLTAGDISP;
+
+ STAILQ_INIT(&srv->access_rules);
+ TAILQ_INIT(&srv->repos);
TAILQ_INSERT_TAIL(&gotwebd->servers, srv, entry);
gotwebd->server_cnt++;
}
free(h);
+}
+
+struct gotwebd_repo *
+gotwebd_new_repo(const char *name)
+{
+ struct gotwebd_repo *repo;
+
+ repo = calloc(1, sizeof(*repo));
+ if (repo == NULL)
+ return NULL;
+
+ STAILQ_INIT(&repo->access_rules);
+
+ if (strlcpy(repo->name, name, sizeof(repo->name)) >=
+ sizeof(repo->name)) {
+ free(repo);
+ errno = ENOSPC;
+ return NULL;
+ }
+
+ return repo;
}
+
+static struct gotwebd_repo *
+conf_new_repo(struct server *server, const char *name)
+{
+ struct gotwebd_repo *repo;
+
+ if (name[0] == '\0') {
+ fatalx("syntax error: empty repository name found in %s",
+ file->name);
+ }
+
+ if (strchr(name, '\n') != NULL)
+ fatalx("repository names must not contain linefeeds: %s", name);
+
+ repo = gotwebd_new_repo(name);
+ if (repo == NULL)
+ fatal("gotwebd_new_repo");
+
+ TAILQ_INSERT_TAIL(&server->repos, repo, entry);
+ server->nrepos++;
+
+ return repo;
+};
+
+static void
+conf_new_access_rule(struct gotwebd_access_rule_list *rules,
+ enum gotwebd_access access, char *identifier)
+{
+ struct gotwebd_access_rule *rule;
+
+ rule = calloc(1, sizeof(*rule));
+ if (rule == NULL)
+ fatal("calloc");
+
+ rule->access = access;
+ rule->identifier = identifier;
+
+ STAILQ_INSERT_TAIL(rules, rule, entry);
+}