All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
   1 gsc — Git service configuration
 
   2 -------------------------------
 
   3 2014-12-19-1306 madduck@madduck.net
 
   5 The following describes a method for the configuration of services, using Git
 
   6 as a backend. A service in this context is e.g. a mail server like Postfix or
 
   7 a DNS server like NSD, which runs on a Unix system and is configured using
 
   8 files usually situated in the /etc directory. When those files are changed,
 
   9 usually some steps have to be taken to fix up permissions, compile/merge
 
  10 files, and reload the service.
 
  12 These steps — editing configuration files and reloading services — are also
 
  13 the domain of so-called configuration management systems, such as CFengine and
 
  14 Puppet. While these are indispensable tools for the general management of
 
  15 machines, it is often the case that the fine-grained configuration of
 
  16 a complex mailserver requires more work to express in terms of the
 
  17 configuration management syntax than there are benefits.
 
  19 gsc is an alternative way of doing things. In a nutshell:
 
  21   - configuration is maintained in Git and something like gitolite can be used
 
  22     to manage fine-grained access control;
 
  24   - upon push, a repo-side hook runs, reads from the repository a number of
 
  25     hosts to poke and proceeds to do just that using SSH forced commands;
 
  27   - a dedicated user on each target machine receives the poke, updates the
 
  28     local Git repository and executes an in-tree Makefile, which is
 
  29     responsible for the integration of the changes into the running service,
 
  30     possibly using in-tree host-specific parameters.
 
  32 A word for the security-conscious
 
  33 '''''''''''''''''''''''''''''''''
 
  34 It is true that one might question the use of in-tree configuration files and
 
  35 the Makefile. After all, it allows anyone with write-access to the Git repo to
 
  36 impersonate the dedicated poke user, though not without leaving a trace.
 
  38 In the end it's a matter of trust and if you're ready to assume that someone
 
  39 who's supposed to be able to configure a given service on a machine can't
 
  40 really do any harm by impersonating the user designed to configure the given
 
  41 service, then you're on the safe side.
 
  43 The benefit of this approach is that it avoids operation as root whenever
 
  44 possible, defering to sudo for just those steps that require root privileges.
 
  46 Set-up by example: nsd
 
  47 ----------------------
 
  49 Let's walk through a simple example and configure the nsd DNS server to be
 
  50 configurable/manageable with gsc.
 
  52   0. We'll start with the assumption that the contents of /etc/nsd is already
 
  53      tracked in a central Git repository and that trusted DNS admins can push
 
  56      We'll also assume that changes to the nsd config should be pushed to two
 
  57      DNS servers, grimble.example.org and grumble.example.org.
 
  59   1. Create a new, password-less (!) SSH keyfile using ssh-keygen on your
 
  60      local machine (which needs a clone of the above Git repo), store the key
 
  61      into .gsc/poke-key[.pub] inside that repo, and edit the public keyfile by
 
  62      prepending the following to the first (and only) line (broken here into
 
  63      two lines for readability):
 
  65        command="make -sC target git_pull update",no-agent-forwarding,
 
  66        no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding
 
  68      You can also add a from="" limit as per the sshd(8) manpage, although
 
  69      there are benefits in not doing that, namely the ability for anyone with
 
  70      access to the repo to poke the servers.
 
  72      Commit the two files .gsc/poke-key and .gsc/poke-key.pub to the
 
  75   2. On the two "poke" hosts, make sure you have Git, make and sudo installed.
 
  76      Then add a new system user, e.g. nsd_update (with group nsd_update,
 
  77      disabled password, home directory /var/local/nsd_update, and shell
 
  80   3. Copy .gsc/poke-key.pub to ~nsd_update/.ssh/authorized_keys and make sure
 
  81      to run `chown -R nsd_update ~nsd_update/.ssh` or similar.
 
  83   4. Clone the Git repo with --shared=group to /etc/nsd (or /etc/nsd3) and
 
  84      similarly chown it, so that the poke user can maintain it without root
 
  85      rights. Consequentially, the user "nsd" needs to be able to read
 
  86      (possibly even write) the files in this repository, so you need to either
 
  87      add nsd to the group nsd_update and ensure g+rwX permissions (using the
 
  88      Makefile, see below), or employ ACLs.
 
  90      Make sure that the nsd_update user can pull from the repo, either by
 
  91      making it publicly available, or by setting up a new password-less SSH
 
  92      key for nsd_update on each host, which is then given access to the
 
  95   5. Create a symlink ~nsd_update/target to /etc/nsd (or whatever directory
 
  96      you chose). This was done to avoid having to make /etc/nsd the
 
  97      home directory of the update user and not hardcoding the location of the
 
 100   6. Now you should be able to poke a host from your workstation:
 
 102        ssh -Ti .gsc/poke-key -o UserKnownHostFile=.gsc/ssh_known_hosts \
 
 103          nsd_update@grimble.example.org
 
 105      which should print something like
 
 107        make: *** No rule to make target 'git_pull'.  Stop.
 
 109      Do this for all hosts in pokehosts to populate .gsc/ssh_known_hosts
 
 110      (verifying the fingerprints, of course), and then commit this file as
 
 113      As we are using forced commands, it makes no difference what you put
 
 114      after the user@host argument in the above. However, we may need this in
 
 115      the future, so let's reserve it. Please refrain from using it and/or let
 
 116      me know if you have any ideas.
 
 118   7. Install the gsc-post-receive hook to hooks/post-receive in the central
 
 119      git repo. When using gitolite, I suggest the use of per-repository hooks
 
 122   8. Commit a file .gsc/pokehosts to the repository, listing each target
 
 123      server one-per-line in user@host style, e.g.
 
 125        nsd_update@grimble.example.org
 
 126        nsd_update@grumble.example.org
 
 128      The rest of each line is unused for now, but reserved for future use, so
 
 129      don't put anything else there.
 
 131   9. Now push your commits to the central repo, which should cause the hook to
 
 132      poke both hosts, yielding the make error.
 
 134  10. Now all that's left to do is write /etc/nsd/Makefile with at least the
 
 135      targets git_pull and update, which should run git-pull (possibly with
 
 136      --rebase) and then cause the appropriate next steps. Use sudo sparingly
 
 137      for maximum security benefit.
 
 139      Please refer to the examples/ directory in the gsc repository for
 
 140      a simple example that works with nsd, and which also handles per-host
 
 141      difference by parametrising e.g. IP addresses or commands needed to
 
 142      reload/restart the service.
 
 147   - how to recover from faulty pushes, e.g. if the server was taken down