summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile20
-rw-r--r--README152
-rw-r--r--apt.conf2
-rwxr-xr-xetckeeper26
-rw-r--r--etckeeper.131
-rw-r--r--foo/changelog5
-rw-r--r--foo/compat1
-rw-r--r--foo/control20
-rw-r--r--foo/copyright5
-rwxr-xr-xfoo/rules33
-rwxr-xr-xinit.d/10restore-metadata5
-rwxr-xr-xinit.d/20git-init6
-rwxr-xr-xinit.d/30git-perm3
-rwxr-xr-xinit.d/40git-ignore14
-rwxr-xr-xinit.d/40git-pre-commit-hook16
-rwxr-xr-xinit.d/50git-add5
-rw-r--r--init.d/README13
-rwxr-xr-xpost-apt.d/50git-add5
-rwxr-xr-xpost-apt.d/60git-rm10
-rwxr-xr-xpost-apt.d/75git-commit9
-rw-r--r--post-apt.d/README2
-rwxr-xr-xpre-apt.d/50uncommitted-changes15
-rw-r--r--pre-apt.d/README2
-rwxr-xr-xpre-commit.d/10store-metadata16
-rwxr-xr-xpre-commit.d/10warn-empty-directory6
-rwxr-xr-xpre-commit.d/10warn-hardlinks7
-rwxr-xr-xpre-commit.d/10warn-special-file9
-rw-r--r--pre-commit.d/README2
28 files changed, 440 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4497b0d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+install:
+ mkdir -p $(PREFIX)/usr/lib/etckeeper/
+ cp -a *.d $(PREFIX)/usr/lib/etckeeper/
+
+ install -D etckeeper $(PREFIX)/usr/bin/etckeeper
+ install -m 0644 -D apt.conf $(PREFIX)/etc/apt/apt.conf.d/05etckeeper
+ install -m 0644 -D etckeeper.1 $(PREFIX)/usr/share/man/man1/etckeeper.1
+
+ for dir in *.d; do \
+ command=$$(echo "$$dir" | sed -e 's/\.d$$//'); \
+ ln -sf etckeeper $(PREFIX)/usr/bin/etckeeper-$$command; \
+ ln -sf etckeeper.1 $(PREFIX)/usr/share/man/man1/etckeeper-$$command.1; \
+ done
+
+ for dir in *.d; do \
+ mkdir -p $(PREFIX)/etc/etckeeper/$$dir; \
+ for file in $$dir/*; do \
+ ln -sf /usr/lib/etckeeper/$$file $(PREFIX)/etc/etckeeper/$$file; \
+ done; \
+ done
diff --git a/README b/README
index e69de29..690b669 100644
--- a/README
+++ b/README
@@ -0,0 +1,152 @@
+etckeeper is a collection of tools to let /etc be stored in a git
+repository. It hooks into apt to automatically commit changes made to /etc
+during package upgrades. It uses `metastore` to track file metadata that
+git does not normally support, but that is important for /etc, such as the
+permissions of `/etc/shadow`. It's quite modular and configurable, while
+also being simple to use if you understand the basics of working with git.
+
+
+## security warning
+
+First, a big warning: By checking /etc into revision control, you are
+creating a copy of files like /etc/shadow that must remain secret. Anytime
+you have a copy of a secret file, it becomes more likely that the file
+contents won't remain secret. etckeeper is careful about file permissions,
+and will make sure that repositories it sets up don't allow anyone but root
+to read their contents. However, you *also* must take care when cloning
+or copying these repositories, not to allow anyone else to see the data.
+
+Since git mushes all the files into packs under the .git directory, the
+whole .git directory needs to be kept secret. Also, since git doesn't keep
+track of the mode of files like the shadow file, it will check it out world
+readable, before etckeeper fixes the permissions. The tutorial has some
+examples of safe ways to avoid these problems when cloning an /etc
+repository.
+
+
+## what etckeeper does
+
+etckeeper has special support to handle changes to /etc caused by
+installing and upgrading packages. Before apt installs packages,
+`etckeeper-pre-apt` will check that /etc contains no uncommitted changes.
+After apt installs packages, `etckeeper-post-apt` will add any new
+interesting files to the repository, and commit the changes.
+
+git is designed as a way to manage source code, not as a way to manage
+arbitrary directories like /etc. This means it has a few limitations that
+etckeeper has to work around. These include file metadata storage,
+empty directories, and special files.
+
+git has only limited tracking of file metadata, being able to track the
+executable bit, but not other permissions or owner info. So file metadata
+storage is handled by `metastore`. Amoung other chores, `etckeeper-init`
+sets up a git hook that use `metastore` to store metadata about file
+owners, permissions, and even extended attributes. This metadata is stored
+in git along with everything else, and can be applied if the repo should
+need to be checked back out.
+
+git cannot track empty directories. So `etckeeper-init` also sets up a git
+hook to run `etckeeper-pre-commit`, which checks for empty directories
+before committing, and warn about them. You can then either ignore the
+empty directory, if it's not significant, or put a file (such as
+`.gitignore`) in the directory to enable git to track it.
+
+git doesn't support several special files that you _probably_ won't have in
+/etc, such as unix sockets, named pipes, hardlinked files (but softlinks
+are fine), and device files. Again git hooks are used to warn if your /etc
+contains such untrackable special files.
+
+
+## tutorial
+
+A quick walkthrough of using etckeeper.
+
+ cd /etc
+ etckeeper-init
+
+This `etckeeper-init` command initialises an /etc/.git/ repository. This
+command is careful to never overwrite existing files or directories in
+/etc. It will create a `.gitignore` if one doesn't already exist, sets up
+git hooks if they don't already exist, and so on. It does *not* commit any
+files into to git, but does `git-add` all interesting files for an initial
+commit. So you might want to use git status to check that it includes all
+the right files, and none of the wrong files. And you can edit the
+.gitignore and so forth. Once you're ready:
+
+ git commit -m "initial checkin"
+ git gc # pack git repo to save a lot of space
+
+After this first checkin, you can use regular git commands to check in
+further changes:
+
+ passwd someuser
+ git status
+ git commit -a -m "changed a password"
+
+Rinse, lather, repeat.
+
+etckeeper hooks into apt so changes to files in /etc caused by installing or
+upgrading packages will automatically be committed. (`etckeeper-post-apt`
+uses `git-add .`, so any new files in /etc that arn't gitignored will be
+added. If it auto-adds files you don't want added, put them in
+`.gitignore`.)
+
+You can use any git commands you like, but do keep in mind that, if you
+check out a different branch or an old version, git is operating directly
+on your system's /etc. Often it's better to clone /etc to elsewhere and do
+potentially dangerous stuff in a staging directory. You can clone the
+repository using git-clone, but be careful that the directory it's cloned
+into starts out mode 700, to prevent anyone else from seeing files like
+shadow, before `etckeeper-init` fixes their permissions:
+
+ mkdir /my/clone
+ cd /my/clone
+ chmod 700 .
+ git clone /etc
+ etckeeper-init
+ chmod 755 .
+
+Another common reason to clone the repository is to make a backup to a
+server. When using git-push to create a new remote clone, make sure the new
+remote clone is mode 700! (And, obviously, only push over a secure
+transport like ssh.)
+
+ ssh server 'mkdir /etc-clone; cd /etc-clone; chmod 700 .; git init'
+ git push ssh://server/etc-clone master
+
+Of course, it's also possible to push from a server onto client machines,
+to deploy changes to /etc. You might even set up branches for each machine
+and merge changes between them. Once /etc is under version control, the
+sky's the limit..
+
+
+## configuration
+
+Each etckeeper-foo command uses `run-parts` to run the executable files in
+/etc/etckeeper/foo.d/. By default these directories contain a bunch of
+symlinks to the actual files; you can remove or reorder the symlinks,
+or add your own custom files.
+
+Note that the etckeeper-foo commands are careful to not hardcode anything about
+git. If you want to use some other revision control system, that's
+theoretically possible to accomplish by just changing the files in
+/etc/etckeeper/. If you do this, please let me know.
+
+
+## inspiration
+
+Two blog posts provided inspiration for techniques used by etckeeper:
+* http://www.jukie.net/~bart/blog/20070312134706
+* http://bryan-murdock.blogspot.com/2007/07/put-etc-under-revision-control-with-git.html
+
+isisetup (http://www.isisetup.ch/) has some of the same aims as etckeeper,
+however, unlike it, etckeeper does not aim to be a git porcelain with its
+own set of commands for manipulating the /etc repository. Instead,
+etckeeper provides a couple of simple tools and hooks for setting up an /etc
+repsository, and then gets out of your way; you manage the repository using
+regular git commands.
+
+
+## author
+
+Joey Hess <joey@kitenet.net>
diff --git a/apt.conf b/apt.conf
new file mode 100644
index 0000000..96b7e14
--- /dev/null
+++ b/apt.conf
@@ -0,0 +1,2 @@
+DPkg::Pre-Install-Pkgs { "if [ -x /usr/bin/etckeeper-pre-apt ]; then etckeeper-pre-apt; fi"; };
+DPkg::Post-Invoke { "if [ -x /usr/bin/etckeeper-post-apt ]; then etckeeper-post-apt; fi"; };
diff --git a/etckeeper b/etckeeper
new file mode 100755
index 0000000..0840e85
--- /dev/null
+++ b/etckeeper
@@ -0,0 +1,26 @@
+#!/bin/sh
+set -e
+
+if [ "$(basename $0)" != etckeeper ]; then
+ command="$(basename $0 | sed -e s/etckeeper-//)"
+else
+ if [ -z "$1" ]; then
+ echo "usage: etckeeper command" >&2
+ exit 1
+ fi
+ command="$1"
+ shift 1
+fi
+
+if [ ! -d "/etc/etckeeper/$command.d" ]; then
+ echo "etckeeper: /etc/etckeeper/$command.d does not exist" >&2
+ exit 1
+fi
+
+if [ "$command" = post-apt ] || [ "$command" = pre-apt ]; then
+ cd /etc
+elif [ "$command" = pre-commit ] && [ -n "$1" ]; then
+ chdir "$1"
+fi
+
+run-parts "/etc/etckeeper/$command.d"
diff --git a/etckeeper.1 b/etckeeper.1
new file mode 100644
index 0000000..bcecf59
--- /dev/null
+++ b/etckeeper.1
@@ -0,0 +1,31 @@
+.\" -*- nroff -*-
+.TH ETCKEEPER 1 "" "" ""
+.SH NAME
+etckeeper \- keep /etc in git
+.SH SYNOPSIS
+.B etckeeper command [args]
+.SH DESCRIPTION
+etckeeper is a collection of tools to let /etc be stored in a git
+repository. Please see its README for more detailed information.
+.SH COMMANDS
+.TP
+.B init
+This is the only command you typically need to run by hand. It initialises
+a git repository for the current directory. Typically this is run in /etc
+once when starting to use etckeeper on a machine. It can also be used to
+initialise a clone of the /etc repository.
+.TP
+.B pre-commit
+This is called as a git pre-commit hook. It should be passed the name
+of the toplevel directory of the repository (typically /etc). It stores
+metadata and does sanity checks.
+.TP
+.B pre-apt
+This is called by apt's DPkg::Pre-Install-Pkgs hook. It allows committing
+any uncommitted changes in /etc before the apt run.
+.TP
+.B post-apt
+This is called by apt's DPkg::Post-Invoke hook. It commits changes made by
+packages to the repository.
+.SH AUTHOR
+Joey Hess, <joey@kitenet.net>.
diff --git a/foo/changelog b/foo/changelog
new file mode 100644
index 0000000..0925dbf
--- /dev/null
+++ b/foo/changelog
@@ -0,0 +1,5 @@
+etckeeper (0.1) unstable; urgency=low
+
+ * First release.
+
+ -- Joey Hess <joeyh@debian.org> Mon, 05 Nov 2007 16:43:04 -0500
diff --git a/foo/compat b/foo/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/foo/compat
@@ -0,0 +1 @@
+5
diff --git a/foo/control b/foo/control
new file mode 100644
index 0000000..01735ab
--- /dev/null
+++ b/foo/control
@@ -0,0 +1,20 @@
+Source: etckeeper
+Section: admin
+Priority: optional
+Build-Depends: debhelper (>= 5), dpkg-dev (>= 1.9.0)
+Maintainer: Joey Hess <joeyh@debian.org>
+Standards-Version: 3.7.2
+Vcs-Git: git://git.kitenet.net/etckeeper
+
+Package: etckeeper
+Architecture: all
+Section: admin
+Depends: metastore, git-core, ${misc:Depends}
+Description: store /etc in git
+ etckeeper is a collection of tools to let /etc be stored in a git
+ repository. It hooks into apt to automatically commit changes made to /etc
+ during package upgrades. It uses `metastore` to track file metadata that
+ git does not normally support, but that is important for /etc, such as the
+ permissions of `/etc/shadow`. It's quite modular and configurable, while
+ also being simple to use if you understand the basics of working with git.
+
diff --git a/foo/copyright b/foo/copyright
new file mode 100644
index 0000000..9ff61e0
--- /dev/null
+++ b/foo/copyright
@@ -0,0 +1,5 @@
+Files: *
+Copyright: © 2007 Joey Hess <joey@kitenet.net>
+License: GPL-2+
+ The full text of the GPL is distributed as doc/GPL in etckeeper's source,
+ and is distributed in /usr/share/common-licenses/GPL-2 on Debian systems.
diff --git a/foo/rules b/foo/rules
new file mode 100755
index 0000000..a759dac
--- /dev/null
+++ b/foo/rules
@@ -0,0 +1,33 @@
+#!/usr/bin/make -f
+
+build:
+ dh_testdir
+
+clean:
+ dh_testdir
+ dh_testroot
+ dh_clean
+
+binary-arch: build
+
+binary-indep: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ $(MAKE) PREFIX=debian/etckeeper
+ dh_installdocs README
+ dh_installexamples
+ dh_installchangelogs
+ dh_compress
+ dh_fixperms
+ dh_installdeb
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+# Not intended for use by anyone except the author.
+announcedir:
+ @echo ${HOME}/src/joeywiki/code/etckeeper/news
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary
diff --git a/init.d/10restore-metadata b/init.d/10restore-metadata
new file mode 100755
index 0000000..fdb1f8e
--- /dev/null
+++ b/init.d/10restore-metadata
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+if [ -e .metadata ]; then
+ metastore --apply
+fi
diff --git a/init.d/20git-init b/init.d/20git-init
new file mode 100755
index 0000000..69867c4
--- /dev/null
+++ b/init.d/20git-init
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -e
+if [ ! -e .git ]; then
+ git-init
+ echo "$(hostname) /etc repository" > .git/description
+fi
diff --git a/init.d/30git-perm b/init.d/30git-perm
new file mode 100755
index 0000000..564e489
--- /dev/null
+++ b/init.d/30git-perm
@@ -0,0 +1,3 @@
+#!/bin/sh
+set -e
+chmod 700 .git
diff --git a/init.d/40git-ignore b/init.d/40git-ignore
new file mode 100755
index 0000000..88866cf
--- /dev/null
+++ b/init.d/40git-ignore
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -e
+if [ ! -e .gitignore ]; then
+ cat >.gitignore <<EOF
+*~
+
+# new and old versions of conffiles, stored by dpkg
+*.dpkg-*
+
+# mount(8) records system state here, no need to keep these in git
+blkid.tab(|.old)
+mtab
+EOF
+fi
diff --git a/init.d/40git-pre-commit-hook b/init.d/40git-pre-commit-hook
new file mode 100755
index 0000000..88eb581
--- /dev/null
+++ b/init.d/40git-pre-commit-hook
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -e
+if [ -x .git/hooks/pre-commit ]; then
+ if ! grep -q etckeeper-pre-commit .git/hooks/pre-commit; then
+ echo "etckeeper warning: .git/hooks/pre-commit needs to be manually modifed to run etckeeper-pre-commit" >&2
+ fi
+else
+ cat >.git/hooks/pre-commit <<EOF
+#!/bin/sh
+# pre-commit hook for etckeeper. Calls etckeeper-pre-commit to store metadata
+# and do sanity checks.
+set -e
+etckeeper-pre-commit `pwd`
+EOF
+ chmod +x .git/hooks/pre-commit
+fi
diff --git a/init.d/50git-add b/init.d/50git-add
new file mode 100755
index 0000000..06504b4
--- /dev/null
+++ b/init.d/50git-add
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+if ! git-add .; then
+ echo "etckeeper warning: git-add failed" >&2
+fi
diff --git a/init.d/README b/init.d/README
new file mode 100644
index 0000000..90aec67
--- /dev/null
+++ b/init.d/README
@@ -0,0 +1,13 @@
+Executable files in this directory are run to initialise the working directory
+for use by etckeeper. If the working directory is not already in version
+control, that includes setting up the version control, but not actually
+committing anything. If the working directory is in version control,
+it includes applying stored metadata to the checked out files in the
+working directory.
+
+Please be careful to *never* overwrite existing files/directories
+in the working directory (or use absolute care when doing so). If a file
+you need to write already exists, check if its contents are sane, and
+if not, emit a warning on stderr.
+
+If initialisation fails, exit nonzero and no later files will be run.
diff --git a/post-apt.d/50git-add b/post-apt.d/50git-add
new file mode 100755
index 0000000..06504b4
--- /dev/null
+++ b/post-apt.d/50git-add
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+if ! git-add .; then
+ echo "etckeeper warning: git-add failed" >&2
+fi
diff --git a/post-apt.d/60git-rm b/post-apt.d/60git-rm
new file mode 100755
index 0000000..73dc4d8
--- /dev/null
+++ b/post-apt.d/60git-rm
@@ -0,0 +1,10 @@
+#!/bin/sh
+set -e
+
+TAB=" "
+
+for file in $(git status | egrep "^#${TAB}*deleted: *" | sed 's/.*deleted: *//'); do
+ if [ ! -d "$file" ]; then
+ git rm "$file"
+ fi
+done
diff --git a/post-apt.d/75git-commit b/post-apt.d/75git-commit
new file mode 100755
index 0000000..30ef0f2
--- /dev/null
+++ b/post-apt.d/75git-commit
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+
+# TODO: figure out what packages were acted on by the apt run, and include
+# that info in the commit message
+message="committing changes after apt run"
+
+# ignore exit code since it exits nonzero if there is nothing to do
+git commit -m "$message" || true
diff --git a/post-apt.d/README b/post-apt.d/README
new file mode 100644
index 0000000..befa5c0
--- /dev/null
+++ b/post-apt.d/README
@@ -0,0 +1,2 @@
+Files in this directory are run after apt has run. They should commit
+changes and new files in /etc to repository.
diff --git a/pre-apt.d/50uncommitted-changes b/pre-apt.d/50uncommitted-changes
new file mode 100755
index 0000000..c8e4736
--- /dev/null
+++ b/pre-apt.d/50uncommitted-changes
@@ -0,0 +1,15 @@
+#!/bin/sh
+set -e
+if ! LANG=C git-status 2>&1 | grep -q "working directory clean"; then
+ git-status || true
+ echo "etckeeper warning: /etc is not clean" >&2
+ printf "Press Enter to commit changes and continue. "
+ read line </dev/tty
+ git add .
+ if ! git commit -m "saving uncommitted changes in /etc prior to apt run"; then
+ echo "etckeeper warning: git commit failed" >&2
+ echo "Please resolve the uncommitted changes by hand."
+ printf "Press Enter when ready to continue. "
+ read line </dev/tty
+ fi
+fi
diff --git a/pre-apt.d/README b/pre-apt.d/README
new file mode 100644
index 0000000..47001b4
--- /dev/null
+++ b/pre-apt.d/README
@@ -0,0 +1,2 @@
+Files in this directory are run before apt is run. This is mostly used for
+sanity checks, ie, does /etc have any uncommitted changes?
diff --git a/pre-commit.d/10store-metadata b/pre-commit.d/10store-metadata
new file mode 100755
index 0000000..7958888
--- /dev/null
+++ b/pre-commit.d/10store-metadata
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -e
+
+# ensure the file exists so that it will list its own metadata
+if [ ! -e .metadata ]; then
+ metastore --save
+ # the file could leak hidden dir contents..
+ chmod 600 .metadata
+fi
+
+# metastore doesn't produce the same output file for the same metadata
+# everytime, so avoid changing the file if nothing really changed.
+if [ ! -z "$(metastore --compare)" ]; then
+ metastore --save
+ git add .metadata
+fi
diff --git a/pre-commit.d/10warn-empty-directory b/pre-commit.d/10warn-empty-directory
new file mode 100755
index 0000000..b850c86
--- /dev/null
+++ b/pre-commit.d/10warn-empty-directory
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -e
+empty=$(find -type d -empty | grep -v /.git/) || true
+if [ -n "$empty" ]; then
+ echo "etckeeper warning: there are some empty directories, which git will ignore" >&2
+fi
diff --git a/pre-commit.d/10warn-hardlinks b/pre-commit.d/10warn-hardlinks
new file mode 100755
index 0000000..3dd7a96
--- /dev/null
+++ b/pre-commit.d/10warn-hardlinks
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -e
+hardlinks=$(find -type f -not -links 1 | grep -v /.git/) || true
+if [ -n "$hardlinks" ]; then
+ echo "etckeeper warning: hardlinked files could cause problems with git:" >&2
+ echo "$hardlinks" >&2
+fi
diff --git a/pre-commit.d/10warn-special-file b/pre-commit.d/10warn-special-file
new file mode 100755
index 0000000..cb4d019
--- /dev/null
+++ b/pre-commit.d/10warn-special-file
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+special=$(find -not -type d -not -type f -not -type l | grep -v /.git/) || true
+if [ -n "$special" ]; then
+ echo "etckeeper warning: special files could cause problems with git:" >&2
+ echo "$special" >&2
+fi
+
+true
diff --git a/pre-commit.d/README b/pre-commit.d/README
new file mode 100644
index 0000000..051d094
--- /dev/null
+++ b/pre-commit.d/README
@@ -0,0 +1,2 @@
+This is run by a git pre-commit hook before committing changes to the
+repository. This can be used for storing metadata, and for sanity checks.