SNARE.CONF(5) File Formats Manual SNARE.CONF(5)

snare.confsnare configuration file

snare.conf is the configuration file for snare(1). It consists of one or more top-level options and one GitHub block.

The top-level options are:

listen =address”;
is a mandatory address and port number to listen on. The format of address is either:
x.x.x.x:port
an IPv4 address and port. For example, ‘0.0.0.0:8765’ will listen on port 8765 on for all IPv4 addresses.
[x:x:x]:port
an IPv6 address and port. For example, ‘[::]:8765’ will listen on port 8765 for all IPv4 and IPv6 addresses.
maxjobs = int;
is an optional non-zero positive integer specifying the maximum number of jobs to run in parallel. Defaults to the number of CPUs in the machine.
user =user-name”;
is an optional username that snare.conf will try and change into after it has bound to a network port. Note that snare.conf will refuse to run as root unless user is specified. As part of changing user, snare.conf:
  • changes its uid, euid, suid to the UID of user-name.
  • changes its gid, egid, sgid to the primary GID of user-name.
  • changes its CWD to the home directory of user-name.
  • sets the $HOME environment variable to the home directory of user-name.
  • sets the $USER environment variable to user-name.

All other environment variables are passed through to per-repo programs unchanged.

github { ... }
specifies GitHub specific options.

A ‘github’ block supports the following options:

reposdir =path”;
is a path to a directory containing per-repo programs (see PER-REPO PROGRAMS for more information).
matchregex” { match-options }
where regex is a regular expression in Rust regex format that must match against a “owner/repo” full repository name. If it matches, then match-options are applied. “regex” must match against the full repository name: in other words, it is equivalent to ^regex$. Thus the regex “a/b” does not match against the full repository name “a/bc”, but the regex “a/b.*” does match against “a/bc”.

A ‘match’ block supports the following options:

email =address”;
optionally specifies an email address to which any errors running per-repo programs will be sent (warning: full stderr/stdout will be sent, so consider carefully whether these have sensitive information or not). This uses the `sendmail` command to send email: you should ensure that you have installed, set-up, and enabled a suitable `sendmail` clone.
queue = (evict | parallel | sequential);
specifies what to do when multiple requests for the same repository are queued at once:
evict
only run one job for this repository at a time. Additional jobs will stay on the queue: if a new job comes in for that repository, it evicts any previously queued jobs for that repository. In other words, for this repository there can be at most one running job and one queued job at any point.
parallel
run as many jobs for this repository in parallel as possible.
sequential
only run one job for this repository at a time. Additional jobs will stay on the queue and be executed in FIFO order.

The default match block sets this to sequential, which is always safe, though at the possible expense of lower job throughput for any given repository.

secret =secret”;
is the optional GitHub secret used to sign the webhook request. This allows snare.conf to tell the difference between genuine webhook requests and those from malfeasants. Although this is optional, we highly recommend setting it in all cases. Note also that if a GitHub request is signed, but you have not specified a secret, then snare will return the request as “unauthorised” to remind you to use the secret at both ends.
timeout = period;
specifies the elapsed time, as a positive integer, in seconds that a process can run before being sent SIGTERM. The default match block sets this to one hour (3600 seconds).

match blocks are evaluated in order from top to bottom with each successful match overriding previous settings. A default match block is inserted before any user match blocks:

match ".*" {
  queue = sequential;
  timeout = 3600;
}

When using snare.conf, the per-repo programs do the actual work of executing specific actions for a given repository. For a repository repo owned by ‘user’, the command

<reposdir>/<user>/<repo> <event> <path-to-GitHub-JSON>

will be run. Per-repo programs must be marked as executable and are run with their current working directory set to a temporary directory to which they can freely write and which will be automatically removed when they have completed.

For example, snare's GitHub repository is https://github.com/softdevteam/snare. If we set up a web hook up for that repository that notifies us of pull request events, then the command:

/path/to/softdevteam/snare pull_request /path/to/json

will be executed, where:

/path/to/softdevteam/snare
is the per-repo program for the “softdevteam” user and the “snare” repository.
pull_request
is the type of the GitHub event.
/path/to/json
is a path to a file containing the complete GitHub JSON for that event.

The softdevteam/snare per-repo program can then execute whatever it wants. In order to work out precisely what event has happened, you will need to read GitHub's webhooks documentation.

The minimal recommended snare.conf file is as follows:

listen = "<address>:<port>";
github {
  reposdir = "<path>";
  match ".*" {
    email = "<email>";
    secret = "<secret>";
  }
}

The top-to-bottom evaluation of match blocks allow users to specify defaults which are only overridden for specific repositories. For example, for the following configuration file:

listen = "<address>:<port>";
github {
  reposdir = "<path>";
  match ".*" {
    email = "abc@def.com";
    secret = "sec";
  }
  match "a/b" {
    email = "ghi@jkl.com";
  }
}

the following repositories will have these settings:

a/b:
  queue = sequential
  timeout = 3600
  email = "ghi@jkl.com"
  secret = "sec"
c/d:
  queue = sequential
  timeout = 3600
  email = "abc@def.com"
  secret = "sec"

Users can write per-repo programs in whatever system/language they wish, so long as the matching file is marked as executable. The following simple example uses shell script to send a list of commits and diffs to the address specified in $EMAIL on each “push” event. It works for any public GitHub repository:

#! /bin/sh

set -euf
EMAIL="someone@something.com"

if [ "$1" != "push" ]; then
    exit 0
fi

repo_fullname=`jq .repository.full_name "$2" | tr -d '
repo_url=`jq .repository.html_url "$2" | tr -d '
before_hash=`jq .before "$2" | tr -d '
after_hash=`jq .after "$2" | tr -d '

git clone "$repo_url" repo
cd repo
git log --reverse -p "$before_hash..$after_hash" \
  | mail -s "Push to $repo_fullname" "$EMAIL"

where jq is a command-line JSON processor. Depending on your needs, you can make this type of script arbitrarily more complex and powerful (for example, not cloning afresh on each pull).

Note that this program is deliberately untrusting of external input: it is careful to quote all arguments obtained from JSON; and it uses a fixed directory name ( “repo”) rather than a file name from JSON that might include characters (such as “../..”) that would cause the script to leak data about other parts of the file system.

snare(1)

snare(1) was written by Laurence Tratt https://tratt.net/laurie/

2020-02-10 OpenBSD 6.6