Let’s say you want to work on a public git repo without revealing your identity.

The real solution is to set up a new computer on a VPN (so you don’t reveal your IP address to the git hosting service) set to a different time zone (so you don’t publish that you’re even in the same general area) with separate git credentials and keys entirely. Make sure you secure the privacy of your anonymous email address, in case it could somehow be correlated to you. Don’t cross-contaminate between your normal coding box and your anonymous box. You’ll probably also want to randomize the timestamps (don’t forget author and committer) so nobody can analyze when you’re awake and deduce a probable location.

But that’s a lot of work.

You can accomplish most of the same thing with a VM on your normal box, but there’s a higher risk of cross-contamination since you’re on the same IP by default, and active on the network at the same times. You also run the risk of potentially sharing files from your main box to your anonymous VM, which could lead to a mistake.

That’s also a lot of work.

If you don’t mind the very likely possibility of errors leading to your potential de-anonymization, then here’s one way to be “anonymous enough” for some purposes. (Until you make a mistake, and the party’s over.)

Since git doesn’t support .gitconfigs in parent directories, you can’t just set a file in the parent of your anonymous checkouts and forget about it. Even if you could, you might still want to use a different time zone from your usual identity, so there’s one less clue. Plus, how would you cleanly separate your normal identity from your secret identity on your git hosting service, when you don’t always control the user you use for ssh access (e.g. github has everyone ssh as git).

You need a way to set your git name and email address for every repo you want to work on anonymously, plus some automated help keeping your identity from leaking.

Try this git clone replacement. The only trick is remembering to use it–maybe make a parent folder for all your anonymous work and throw it in there named something like OH-GOD-DONT-REGULAR-GIT-CLONE--USE-ME-INSTEAD so at least when you ls before a clone you’re likely to be reminded. You’ll also need some ssh config, below.

Before I explain, here’s the required ~/.ssh/config change. You’ll of course need to generate your ~/.ssh/id_rsa-github-private key in addition. This all needs to be in place before running the script.

Host github.private
	HostName github.com
	User git
	IdentityFile ~/.ssh/id_rsa-github-private

The clone script does a few things. First, it rewrites the clone URL (which is required to be like git@github.com..., though it wouldn’t be hard to support other services too) to a made-up host github.private. The ssh config reroutes all ssh requests at github.private to the right place, with your special private github key. This is how you can keep your private ssh identity separate from your normal one, on the same box.

Then it makes a new git template directory with the default contents plus a fancy post-commit hook. The hook checks the commit-in-progress for the specific private name/email address you want to use, and that the timestamps are in UTC, rewriting it if necessary.

Finally, it does the clone, also setting up its config so at least your name and email address are protected even if you evade the post-commit hook.

Speaking of: one downside is that rebasing becomes a huge pain, if you care about UTC timestamps. Without extra work, it’ll rewrite the commit time (but not author time) in your normal time zone. Luckily, there is a solution, but it’s also a lot of work (see my above points about doing anonymous git the right way), so my recommendation is to just not rebase.

It’s not perfect, but sometimes, it’s good enough.

When I was first learning how to use Docker, I ran into an interesting problem. I was trying to make a container with all the services needed to test an email client: SMTP and IMAP daemons, any services useful for debugging, plus any dependencies of those. Docker is perfect for this — one command to bring up a testbed environment, one command to shut it down and return it to a pristine state — but most of the Docker docs assume one process per container. I stumbled around a bit trying to figure out the best way to run all the processes I’d need. After I had a solution that seemed to work, as I was playing around inside the container, I would notice zombie processes piling up. Also, I found that running docker stop on the container would hang for a few seconds, and none of the running daemons would log any indication of being shut down before they were killed.

As I came to realize, these anomalies were caused by the process I was using to start my container. Let’s take a look at what I was doing, then I’ll go into more details about the problem and how to fix it. The full source is available on github, but I’ll summarize the relevant parts here for clarity.

Here’s the Dockerfile I was using:

FROM ubuntu
MAINTAINER Charles Lindsay

RUN apt-get update && apt-get upgrade
# ...Other stuff that sets things up so these packages install without error...
RUN apt-get install -y openssh-server dovecot-imapd postfix
# ...Other stuff to configure those packages...

# Set root password so we can log in.
RUN echo "root:root" | chpasswd

ADD start.sh /start.sh

ENTRYPOINT ["/start.sh"]

Basically, we’re installing the dovecot-imapd and postfix packages and openssh-server (which lets us log in to diagnose mail issues), setting up the system so everything will run without error, then telling Docker to run our start.sh script when it runs the container.

Here’s start.sh:

#!/bin/sh

/usr/sbin/sshd
/usr/sbin/dovecot
/usr/sbin/postfix start

exec /bin/sleep 999d

You can probably already tell this is going to be janky. Why do we need to sleep at the end? Docker will shut down the whole container when the process it runs for the container exits, so we need the script to hang until we explicitly kill it. We could instead run sshd or dovecot last and in the foreground (postfix has no option to run in the foreground, so that won’t work), but which one should it be? And what happens if you need to restart that service while the container is still running? You’d kill the whole container.

Because containers run in their own process namespace, whatever command you tell Docker to run becomes PID 1 inside the container. PID 1, more commonly known as init, has certain responsibilities in Unix systems:

  • Run processes necessary to boot the system into a usable state.
  • Gracefully stop running processes when the system is going down.
  • Stay in the background as long as the system is up, reaping any terminated orphan processes (zombie processes).

Init probably has other responsibilities (see man init), but those are the ones we need to know about for containers.

With this in mind, it should be easy to see why my start.sh script approach was problematic: it completely ignores the middle bullet point and half of the last one. There’s probably some bash magic involving trap and wait that could get you closer to what’s needed (and if you look at the repo, you’ll see I started down that road but quickly gave up). Instead, I wanted a more general solution.

The Docker docs have a couple of examples of running multiple processes using Supervisor or CFEngine, but both of those seemed like overkill for just running a couple of daemons. Also, it could be handy to be able to SSH in and stop a server manually without having to work around a process management tool. Around this time, the baseimage-docker scare piece hit the news, so I knew I wasn’t alone in trying to solve this problem. Still, I wanted something that was simpler and gave me more control than baseimage-docker.

Not finding any existing projects that fit my needs, I set out to make my own. I call my solution minit, a minimalistic init implementation. It’s a simple C program that handles the duties of PID 1 inside Docker containers. Here are some simplified highlights of its operation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static volatile pid_t shutdown_pid = 0;
static volatile int terminate = 0;

// SIGCHLD handler:
static void handle_child(int sig) {
    for(pid_t pid; (pid = waitpid(-1, NULL, WNOHANG)) > 0; ) {
        if(pid == shutdown_pid)
            shutdown_pid = 0;
    }
}

// SIGTERM/SIGINT handler:
static void handle_termination(int sig) {
    terminate = 1;
}

static pid_t run(const char *filename) {
    ...fork & exec, return child PID...
}

int main(int argc, char *argv[]) {
    ...Set up our signal handlers...

    run("/etc/minit/startup");

    while(!terminate)
        ...Suspend until we receive a signal...

    shutdown_pid = run("/etc/minit/shutdown");
    while(shutdown_pid > 0)
        ...Suspend until we receive a signal...

    kill(-1, SIGTERM);
    while(wait(NULL) > 0)
        continue;

    return 0;
}

Following the logic in main: after first setting up the signal handlers, we run the startup program, where container authors can define what services will be started when the container starts. Then, we wait around in the background for the terminate flag to be set by our SIGTERM handler (line 14). While we’re in the background, we’re calling wait on any terminated children (line 6), inherited or otherwise, so zombies don’t pile up. Once we’ve gotten the signal to terminate, we run the shutdown program, where container authors can define any behavior that needs to happen before all processes in the container are killed. We capture the shutdown program’s PID in shutdown_pid and check for its termination in our SIGCHLD handler (line 7) because we need to let it finish on its own before the final step: sending all running processes a SIGTERM and waiting for them all to end before exiting ourselves.

I’ve left out some esoteric signal-handling details and a couple bonus features here, but the full program is still pretty simple, weighing in at fewer than 150 lines including the license and other comments. Most importantly, minit covers all the above bullet points on init’s responsibilities inside containers.

Now that we have a solution to the problems I mentioned above, let’s change the container to use it (though since the original code was for a former place of employment, I won’t update the code on github). We have to add minit now, and move our script to where minit can find it. We’ll also rename the script from start.sh to startup, and simply get rid of the sleep line at the end. Here are the final few lines of the new Dockerfile:

ADD minit /sbin/minit
ADD startup /etc/minit/startup

ENTRYPOINT ["/sbin/minit"]

It’s now a hard requirement that we use the exec form of ENTRYPOINT (i.e. inside []) so Docker executes minit directly instead of starting it under a shell.

Sure enough, when I run this version, no zombie processes pile up and docker stop shuts things down immediately and gracefully. Problems solved!