Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bmoor

Tracks FreeBSD configuration in git and records the installed package set, so a host can be inspected, reverted, or rebuilt from the repository.

Ownership, permissions and flags are stored in an mtree(8) spec alongside the files. Secrets are encrypted with age(1). Default tracked paths are /etc, /usr/local/etc, /root/.ssh, /var/cron/tabs and a few files under /boot.

The name is moor, as in mooring point.

Requirements

git and age from packages. Everything else is base. Tested on FreeBSD 15.1.

Install

make install
cp /usr/local/etc/bmoor.conf.sample /usr/local/etc/bmoor.conf
$EDITOR /usr/local/etc/bmoor.conf
bmoor init

Put the repository on its own dataset first. Inside the boot environment, activating an older BE reverts the git history with it:

zfs create -o mountpoint=/var/db/bmoor -o compression=zstd zroot/bmoor

Use

bmoor status                # what would be committed
bmoor commit -m "message"   # exits 1 if nothing changed
bmoor be [name]             # boot environment
bmoor restore --help

History is plain git:

git -C /var/db/bmoor/repo log -p tree/etc/rc.conf
git -C /var/db/bmoor/repo diff HEAD~5 -- tree/usr-local-etc

periodic(8) commits daily via 410.bmoor. The pkg triggers write /var/db/bmoor/dirty and do not commit.

Repository layout

repo/tree/<dir>/      mirrors of the tracked directories
repo/secrets/<dir>/   age-encrypted files, *.age
repo/files/           individually tracked files
repo/manifest/        mtree specs, package and system state
repo/restore.sh       standalone restore script

Patterns

EXCLUDES and SECRETS are read by both tar(1) and mtree(8). Only two forms behave the same in both:

*.sample        basename glob, no '/'
./ssl/certs     literal path anchored at './', relative to each DIRS entry

These match nothing in at least one of the two, and do so silently:

/etc/passwd     leading '/'
**/*.sample     '**'
etc/make.conf   unanchored path
./ssl/*         path plus wildcard

bmoor warns about all four at sync.

Secrets

Files matching SECRETS stay out of the mirror and are stored as age(1) blobs under repo/secrets/. Re-encryption is driven by an HMAC of the plaintext, keyed by DIGEST_KEY, so unchanged secrets do not produce a new blob on every commit.

Use at least two recipients in recipients.txt. Keep IDENTITY and DIGEST_KEY off the repository; without them nothing decrypts.

Restore

restore.sh is copied into the repository on every commit and runs standalone.

sh restore.sh --help
sh restore.sh --root /tmp/t --no-pkg    # into a scratch tree

Order: passwd database and pkg config, package replay, then the full tree and metadata. pkg lock is applied last.

Limits

  • Package versions are not restorable. Restore installs the recorded names at whatever version the repository serves. pkg.freebsd.org keeps no version history.
  • Packages with %R = unknown-repository cannot be restored. Warned at capture.
  • The version gate is exact. A 15.1-RELEASE-p1 capture will not restore onto p2 without --force.
  • base/* packages are recorded but never replayed.
  • Set REMOTE, or the repository only survives as long as the disk.
  • pkg writes /var/backups/pkg.sql.zst nightly via 411.pkg-backup. Include /var/backups in off-host copies.

Not implemented

  • ACLs, extended attributes, hardlinks, device nodes, mtimes. ACLs and hardlinks are detected and warned about.
  • ZFS snapshots at capture time.
  • Automatic boot environments before pkg upgrade. Run bmoor be && pkg upgrade.

Why not etckeeper

No FreeBSD port exists. Its pkg integration is a plugin, which cannot load under pkg-static, and ports set PKG_BIN=pkg-static. Its pkgng shell paths use GNU xargs -d and GNU sed -i. It does not record the package set.

Testing

make test    # 45 assertions, unprivileged
make lint    # sh -n over every script

License

BSD-2-Clause. See LICENSE.

About

Track FreeBSD configuration and package state in git - including the ownership, permissions and flags git discards. mtree(8) for metadata, age(1) for secrets, and a self-contained restore script that ships inside the repo.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages