--- a/README.md Tue Sep 10 22:10:18 2019 -0500
+++ b/README.md Tue Sep 10 23:41:43 2019 -0500
@@ -3,31 +3,74 @@
hgkeeper is an server for [mercurial](https://www.mercurial-scm.org/)
repositories. It provides access control for SSH access.
+It's original design is to be run in a container, but there are plans to make +it run stand-alone as well. hgkeeper is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE version 3.
This project is brand new and not even functional yet... But if you're
interested in helping, please do!!
+The initial setup of hgkeeper has a few steps. Since hgkeeper is an SSH server +you will need to generate host keys for it, as well as create the initial +hgkeeper repository which contains the configuration for your install. +You can generate SSH host keys for whatever type you like, but rsa will cover +just about everyone. That said, a lot of people prefer to use ed25519 as well. +By default the SSH host keys will be looked for in the directory `host-keys` in +the current working directory. This can be changed with the `--ssh-host-keys-path` or `-H` command line arguments to hgkeeper. +This directory will be read and files in it will attempt to be loaded into the server. +To generate a host key you can use the following command, note that you can +create other types via the `-t` option, but you should read the `ssh-keygen` +documentation as other options are avaiable for each type. +$ ssh-keygen -t rsa -b 4096 -o host-keys/ssh_host_rsa_key +### Create the hgkeeper repo +Before you can run the server we need to create the hgkeeper admin repository. +This can be done via `hgkeeper setup`. You will need to pass the arguments +`--admin-username` which is the name of the admin user, as well as +`--admin-pubkey` which is the path to the SSH public key for the new admin +user. By default this we create a new repository under `repos/hgkeeper`. +There are some additional options which you can discover via +`hgkeeper setup --help`. +Once the SSH host keys and the hgkeeper repository are created, you can run +hgkeeper with `hgkeeper serve`. There are some other options that are +available so be sure to check out `hgkeeper serve --help`. hgkeeper has a couple modes of operation but `serve` is the main mode.
The `setup` command is used to bootstrap hgkeeper. It will create the
directory for the repositores, the hgkeeper repository, and create an initial
The `serve` command is the main mode of operation which is to provide access to
Access control is defined in the `hgkeeper` repository that is created via the
`hgkeeper setup` command. It is implemented via [casbin](https://casbin.org)
--- a/setup/command.go Tue Sep 10 22:10:18 2019 -0500
+++ b/setup/command.go Tue Sep 10 23:41:43 2019 -0500
@@ -7,12 +7,16 @@
"bitbucket.org/rw_grim/hgkeeper/globals"
"bitbucket.org/rw_grim/hgkeeper/hg"
+ AdminUsername string `kong:"flag,name='admin-username',help='the name of the initial admin user',required='true'"` + AdminSSHPubkey string `kong:"flag,name='admin-pubkey',help='the path to the ssh pubkey to use for the admin',required='true',type='existingfile'"` @@ -69,8 +73,36 @@
- if err := ioutil.WriteFile(absname, FSMustByte(false, name), 0644); err != nil {
+ // If this is a template file, execute it + if filepath.Ext(name) == ".template" { + absname = absname[:len(absname)-len(".template")] + tplate, err := template.New(name).Parse(FSMustString(false, name)) + file, err := os.Create(absname) + AdminUsername: c.AdminUsername, + if err := tplate.Execute(file, tplateData); err != nil { + fmt.Printf("error: %v\n", err) + // If it's not a template just write it out + if err := ioutil.WriteFile(absname, FSMustByte(false, name), 0644); err != nil { rel, err := filepath.Rel(path, absname)
@@ -83,6 +115,29 @@
+ // create our keys directory + keysDir := filepath.Join(path, "keys") + if err := os.MkdirAll(keysDir, 0755); err != nil { + // now copy the admin key into the keys directory + pubkey, err := ioutil.ReadFile(c.AdminSSHPubkey) + adminPubkey := filepath.Join(keysDir, c.AdminUsername) + relAdminPubkey, err := filepath.Rel(path, adminPubkey) + filenames = append(filenames, relAdminPubkey) + if err := ioutil.WriteFile(adminPubkey, pubkey, 0644); err != nil { if err := runCmd(hg.Add(path, filenames...)); err != nil {
--- a/setup/resources/policy.csv Tue Sep 10 22:10:18 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-# This is the default hgkeeper access policy. If you have not yet read
-# README.md in this directory, please read it first.
-# This file contains the access policies as well as groups for all access
-# The format of each policy is:
-# p, user/group, pathspec, permission, effect
-# * p is required to define that this is a policy.
-# * user/group is the name of the user or group that this policy is affecting.
-# * pathspec is glob like pattern of repositories to affect.
-# * permission is one of read, write, or init. If a user has write access, they
-# also have read access. Likewise, init access grants read and write access
-# * effect is one of allow or deny.
-# More than one policy can match, but if any of the matching policies is a deny,
-# then the deny is honored and the user is denied permission.
-# allow all authenticated users to read everything
-p, public, /*, read, allow
-# give users in the admins group the ability to create repositories everywhere.
-p, admins, /*, init, allow
-# deny authenticated, but not explicitly defined users read access to the
-p, public, /hgkeeper, read, deny
-# The format of a group is as follows:
-# * g is required to define that this is a group.
-# * user is the username that is being added to the group.
-# * group is the name of the group.
-# To add your user to the admins group you would replace my-username with your
-# username in the following example:
-# g, my-username, admins
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/setup/resources/policy.csv.template Tue Sep 10 23:41:43 2019 -0500
@@ -0,0 +1,45 @@
+# This is the default hgkeeper access policy. If you have not yet read +# README.md in this directory, please read it first. +# This file contains the access policies as well as groups for all access +# The format of each policy is: +# p, user/group, pathspec, permission, effect +# * p is required to define that this is a policy. +# * user/group is the name of the user or group that this policy is affecting. +# * pathspec is glob like pattern of repositories to affect. +# * permission is one of read, write, or init. If a user has write access, they +# also have read access. Likewise, init access grants read and write access +# * effect is one of allow or deny. +# More than one policy can match, but if any of the matching policies is a deny, +# then the deny is honored and the user is denied permission. +# allow all authenticated users to read everything +p, public, /*, read, allow +# give users in the admins group the ability to create repositories everywhere. +p, admins, /*, init, allow +# deny authenticated, but not explicitly defined users read access to the +p, public, /hgkeeper, read, deny +# The format of a group is as follows: +# * g is required to define that this is a group. +# * user is the username that is being added to the group. +# * group is the name of the group. +# This value was adding during when the setup command was run to add +# {{.AdminUsername}} to the admins group. +g, {{.AdminUsername}}, admins