pi-hoole
pi-hoole
is a collection of tools to enforce access control for self-hosted
pijul repositories. It can be seen as a cgit-like solution, for authenticated
(public key, SSH) and anonymous accesses. You can grant read and write accesses
to a whole repository, or to a determined subset of branches.
pi-hoole
is distributed under the terms of the AGPL v3.
Getting Started
Building From Source
It should not be a surprise that pi-hoole
is versioned under pijul
, with the
patch format introduced by pijul-0.9
. Fair warning, use pijul-0.10.1
and you
should be able to clone the pi-hoole
repository.
pijul clone https://pijul.lthms.xyz/pijul/pi-hoole
Under the hood, pi-hoole
is implemented in Haskell. We will need stack
to
build it.
cd pi-hoole
stack build
We are using the latest lts available, but pi-hoole
has already been built
with lts-10
(but does not build with older ones).
This build three executables:
pi-hoole-cfg
generates a .authorized_keys
file to enforce access control
for SSH.
pi-hoole-shell
is called to determine if a authenticated user is allowed to
perform a given pijul
command.
pi-hoole-web
is a HTTP proxy to enforce access control for anonymous
requests.
Installing
In order to use pi-hoole
on your server, the first step is to create a new,
dedicated user (e.g. pijul
). You then need to make pijul
and
pi-hoole-shell
available to this user. Currently, we do not provide any
packaging solution to that end, but this might change in the future. Although it
is not mandatory, we consider both pi-hoole-cfg
and pi-hoole-web
have also
been made available for the pijul
user.
Configuring
pi-hoole
executables assume the configuration files are stored at
${XDG_CONFIG_DIRECTORY}/pi-hoole
, that is ~/.config/pi-hoole
by default.
In this directory, pi-hoole
will scan the keys/
directory, if it exists,
to know the list of authorized authenticated users. One user may have as many
public key as required. Keys should be saved in file with the following filename
scheme: <user>(\.<label>)?\.pub
. Thus, lthms.pub
, lthms.laptop.pub
and
lthms.work.pub
are three valid public key filenames for the lthms
user.
To generate a .ssh/authorized_keys
, use pi-hoole-cfg
.
pi-hoole-cfg generate > ~/.ssh/authorized_keys
Resulting file will be of the forms:
command="pi-hoole-shell \".lthms\" \"${SSH_ORIGINAL_COMMAND}\"",no-port-forwarding,no-x11-forwarding,no-agent-forwarding <ssh_keys_1>
command="pi-hoole-shell \".lthms\" \"${SSH_ORIGINAL_COMMAND}\"",no-port-forwarding,no-x11-forwarding,no-agent-forwarding <ssh_keys_2>
To configure the authorized accesses for the users, you have to set up a valid
pi-hoole
configuration file. The latter uses the YAML syntax, and is located
at ${XDG_CONFIG_DIRECTORY}/pi-hoole/config.yaml
. It has two root fields:
groups
and repositories
.
You can define groups of users, and grant certain rights to a group, effectively
granting there rights to each users who are member of the group.
Group names are prefixed by +
, and user names are prefixed by .
.
Then, if you want to create one group ogma
which contains the users lgeorget
and lthms
:
groups:
+ogma: [.lthms, .lgeorget]
You can create as many groups as you want, and one user may be added to several
groups. Currently, nested groups are not allowed (i.e. a group being member of
another group).
The repositories
field allows for defining which repositories are available, and
what given users can do with these repositories. The contents of the
repositories
is a map, where keys are path
to the repositories (relative to
HOME
directory), and values are maps from role to rights.
For instance, given the following configuration:
repositories:
my/first/repo:
.lthms: +w
+contrib: +r +w[master]
anon: +r
my/second/repo:
.lthms: +r
.lgeorget: +w
We declare two repositories, one located at ${HOME}/my/first/repo
, and another
at ${HOME}/my/second/repo
. The user lthms
(identified with .lthms
) can
read and write to this repository, to arbitrary branches. The members of the
group contrib
can read to any branch, but can only write to the master
branch. Finally, the anonymous user (through HTTP), can read to any branch.
Read means being able to clone or pull. Write means being able to push.
For the second repository, lthms
can read to any branch, when lgeorget
can
read and write to any branch. Therefore, it is not possible to clone
my/second/repo
through HTTP, because anon
has not been granted any
particular rights.
Setting Up HTTP Proxy
pi-hoole-web
is a very simple HTTP proxy. It receives HTTP request, ideally
issued by a pijul
client, and turns them into pijul
commands if the anon
role has been granted the required rights.
Currently, pi-hoole-web
cannot be configured, and listen the port 8080. Also,
it does not daemonify itself. The easiest way to set up a pi-hoole-web
instance in a reliable way is to use systemd, for instance with the following
unit:
[Unit]
Description=HTTP Proxy for Pijul anonymous accesses
[Service]
Type=simple
User=pijul
Group=pijul
ExecStart=/usr/bin/pi-hoole-web
Restart=of-failure
TimeoutStopSec=300
[Install]
WantedBy=multi-user.target
For reference, the instance of pi-hoole-web
responsible for pijul.lthms.xyz
is behind a nginx server. One very straightforward nginx configuration can be:
server {
listen 80;
server_name pijul.lthms.xyz;
access_log /var/log/nginx/pijul.lthms.xyz.access.log;
error_log /var/log/nginx/pijul.lthms.xyz.error.log;
location / {
proxy_pass http://localhost:8080/;
}
}
For pi-hoole-web
to work correcty, it needs an additional configuration file,
assumed to be at ${XDG_CONFIG_DIRECTORY}/pi-hoole/web.yaml
. This file is
currently very simple, it contains only three fields:
title
: The title of the summary page
baseUrl
: The base URL used to clone the repositories
example
: Should be one of the public repositories
Here is an example:
title: "my repositories"
baseUrl: "https://my.repo.com"
example: "pi-hoole"
If you add a file called description
in the .pijul
directory of your public
repositories, its content will be used by pi-hoole-web
when it gives a summary
of the public repositories.
Limitations
Private Branches
The command pijul patch
does not have an argument to specify the branch from
which a patch is initially fetched; as a consequence, having read access to a
repo, even for another branch where the patch is not applied, is enough. For
this reason, if a user who obtains a hash for a patch of a branch they cannot
access can fetch the patch. That is, a patch is as private as its hash. Private
branches will eventually be supported in a better manner, but for now, private
branches should mean separated repositories.