These are old notes made during development and only preserved for reference. They may be incomplete or not make any sense. You probably want to read this instead.
I don’t host my dotfiles repo on cloud service such as GitHub, only on my private Gitolite server. I may use my keybase to host it privately on kbfs.
Keybase uses TripleSec, a simple, triple-paranoid, symmetric encryption library borne of keybase.
Many people store dotfiles in version control systems such as git
and there are several existing projects, such as:
I am uncomfortable allowing a some third-party tool to write into my home directory, so I decdided this is an occasion where wheel reinvention is called for…
Here is an exampe…
YADR will take over your
~/.gitconfig
, so if you want to store your usernames, please put them into~/.gitconfig.user
The cryptographic heavy-lifting has alread been implemented as git-crypt, available on GitHub. An Archlinux package for the latest version, currently 0.5.0 (released 20150531), is available on the AUR. There is also a package for the master HEAD.
The following prototype was sufficient to prove the concept:
#!/bin/sh
# /usr/bin/git-dot
exec git --git-dir="$HOME/.git-dot" --work-tree="$HOME" "$@"
That isn’t a lot of code for a functioning dotfiles repo. It allows, for example:
$ git dot commit ...
The --git-dir
option to git
allows an alternative to .git
to be specified, so we can use something like ~/.git-dot
(an alternative is to use the GIT_DIR
environment variable).
The git dot
script is little more than a layer of helpers wrapping git
and
git crypt
commands that could otherwise be given longhand. These helpers
make the experience more transparent. Here is a command summary:
git dot init
performs a git init
, a git crypt init
and writes an exclude list (similar to .gitignore
) to $GIT_DIR/info/exclude
.git dot clone
performs a git clone
to clone a bare repository into $GIT_DIR
and then configure it for use by the working copy in $HOME
.git dot encrypt
adds file specifiers to .gitattributes
as required by git-crypt and commits it.git dot lock
and git dot unlock
do what they say. They do nothing if already in their target state.git dot locked
returns yes
if the repo is in a locked state, otherwise no
.git dot repo
returns yes
if a repo is configured, otherwise no
Other git
or git crypt
commands may be given as git dot ...
or git dot crypt ...
.
The $GIT_DIR/info/exclude
file works similarly and in addition to .gitignore'. The
git dot init` command prepopulates this file:
*
!.*
!.*/**
*.*~
$GIT_DIR
The repo is initilaised as a bare repo at $HOME/.git-dot
and then reconfigured as
a non-bare repo with a working directory at $HOME
.
The basic .git/config
is
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = git@git:dotfiles.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
What git dot
produces:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
worktree = /home/alice
[filter "git-crypt"]
smudge = \"git-crypt\" smudge
clean = \"git-crypt\" clean
required = true
[diff "git-crypt"]
textconv = \"git-crypt\" diff
The below command can be used to move files from the backup into the working directory if they are not encrypted:
$ comm -12 \
<(git dot crypt status | grep '^not' | awk '{print $3}' | sort -u) \
<(cd git-dot-backup.S1az0cs1.tmp; find -type f -printf '%P\n' | sort -u) | \
( cd git-dot-backup.S1az0cs1.tmp; xargs -I {} sh -c 'cp --parents -l -f {} "$HOME"; rm {}')
It just works:
$ git dot init
$ git dot encrypt .netrc .ssh/id_rsa .gnupg/private-keys-v1.d/\*.key
$ git dot add ssh/id_rsa .gnupg/private-keys-v1.d/*.key
$ git dot commit -m "encrypted files"
$ git dot crypt export-key repo-key
Push to another local filesystem location:
$ git init --bare /path/to/dotfiles.git
$ git dot remote add transfer /path/to/dotfiles.git
$ git dot push -u transfer master
To reset $HOME
(for testing purposes) only requires removal of the git dot
files:
$ cd ~
$ rm -rf .git-dot .gitattributes .git-crypt
$ rm -rf ~/.git-dot .git-crypt .gitattributes
$ git dot init
$ git dot crypt add-gpg-user 22D05A45
$ git dot encrypt $(file .ssh/* | grep 'PEM RSA private key' | awk -F ':' '{print $1}' )
$ git dot add $(file .ssh/* | grep 'PEM RSA private key' | awk -F ':' '{print $1}' )
$ git dot encrypt .gnupg/{openpgp-revocs.d/\*.rev,private-keys-v1.d/\*.key}
$ git dot add .gnupg/{openpgp-revocs,private-keys-v1}.d
$ git dot encrypt .netrc .gist
$ git dot add .netrc .gist
$ git dot commit -m 'Encrypted dotfiles'
Set up remote and push
$ git dot remote add origin git@git:dotfiles
$ git dot push -f -u origin master
I used -f
to force-push because I wanted to overwrite an old remote. future pushes can be done by git push
ssh:
$ git dot add $(file .ssh/* | grep 'OpenSSH RSA public key' | awk -F ':' '{print $1}' )
$ git dot reset .ssh/authorized_keys # unstage - added by prior cmd
$ git dot add .ssh/config
$ echo .ssh/authorized_keys >> ~/.gitignore
$ echo .ssh/known_hosts >> ~/.gitignore
$ git dot add .gitignore
$ git dot commit -m 'SSH non-sensitive files: config and public keys'
bash
$ echo .bash_history >> ~/.gitignore
$ git dot add .bash_logout .bash_profile .bashrc .gitignore
$ git dot commit -m "Bash dotfiles; Ignore .bash_history"
gpg:
$ echo .gnupg/random_seed >> .gitignore
$ git dot add .gnupg/{gpg{,-agent}.conf,pubring.kbx,trustdb.gpg} .gitignore
$ git dot commit -m .gnupg
Other host
$ git dot clone git@git:dotfiles
Observations:
.git-crypt
does not exist until add-gpg-user
is used. It contains the added GnuPG key and a copy of .gitattributes
.The copy file .git-crypt/.gitattributes
contains this header:
# Do not edit this file. To specify the files to encrypt, create your own
# .gitattributes file in the directory where your files are.
There’s no verbose option to git-dot
because I didn’t want to interfere with
the arguments to git
and git-crypt
. However, to output the git
commands that
git dot
issues, Duplicate the command
line in the git
function and change the
command
to echo >&2
:
echo >&2 git --git-dir="$GIT_DIR" --work-tree="$HOME" "$@"
command git --git-dir="$GIT_DIR" --work-tree="$HOME" "$@"
Use a test user
sudo userdel -r alice
sudo useradd -m alice
sudo cp repo-key ~alice
sudo cp -a .ssh ~alice/
sudo chown -R alice: ~alice/.ssh
sudo -i -u alice
Then
$ git dot clone git@git:dotfiles.git
Cloning into bare repository '/home/alice/.git-dot'...
Enter passphrase for key '/home/alice/.ssh/johnlane_rsa':
remote: Counting objects: 144, done.
remote: Compressing objects: 100% (132/132), done.
Receiving objects: 100% (144/144), 498.81 KiB | 0 bytes/s, done.
remote: Total 144 (delta 45), reused 0 (delta 0)
Resolving deltas: 100% (45/45), done.
The following files are now encrypted:
.ssh/id_rsa
The original files have been moved to './git-dot-backup.QGKRgVMu.tmp'.
Unlock the encrypted files before moving them back.
Your working directory is unclean. Please stash changes before unlocking.
(use "git dot stash" before and "git dot stash pop" after unlocking)
The following example demonstrates a clone operation:
#!/bin/sh
git config --global user.email "alice@example.com"
git config --global user.name "Alice"
git="git --git-dir=/home/alice/.git-dot --work-tree=/home/alice"
$git clone --bare git@git:dotfiles /home/alice/.git-dot
$git config core.bare false
$git config core.logallrefupdates true
$git reset
backup=$(mktemp -p . -d)
$git ls-tree -r master --name-only | xargs -I f cp -a --parents -l f "$backup" 2>/dev/null
$git checkout .
$git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
$git config branch.master.remote origin
$git config branch.master.merge refs/heads/master
$git config filter.git-crypt.smudge '"git-crypt" smudge'
$git config filter.git-crypt.clean '"git-crypt" clean'
$git config diff.git-crypt.textconv '"git-crypt" diff'
exit
cat <<-EOF > "/home/alice/.git-dot/info/exclude"
*
!.*
!.*/**
*.un~
*.*~
.git-dot
EOF
There is an issue with cloning a repo where the first command outputs error messages, one per encrypted file. The command works and subsequent commands do not report any errors. This is rised as issue github-git-crypt-108.
The issue is worked around in the git-dot
script.