comin is a pull mode NixOS deployment tool: it polls Git repositories and deploys new commits. It can now verify these commits are signed, to exclude attacks on the remote Git repository.

To automate the deployment of your NixOS machines with comin, you only need to add 3 lines in your configuration. It is really easy to use because you don’t need to configure a CICD nor store secrets. However, it requires you to trust the remote Git repository. When the Git repository is compromised, the attacker controls what is deployed on your machines.

To address this problem, comin can now verify Git commit signatures before deploying a commit.

Configure comin to verify Git commit signature Link to heading

  1. Export your GPG public key with gpg --armor --export alice@cyb.org > ./public-key.gpg

  2. Provide to comin a list of GPG public keys (via the attribute gpgPublicKeyPaths)

      services.comin = {
        enable = true;
        gpgPublicKeyPaths = [ ./public-key.gpg ];
        remotes = [{
          name = "origin";
          url = "https://git.com/your/infra.git";
        }];
    
  3. Create a signed commit with git commit -a -S -m 'Signed commit' and push it to your remote.

And that’s all.

When comin fetches a commit, it now checks the commit has been signed by one of these keys. If the fetched commit is not signed by at least one of these keys, comin refuses to deploy this commit.

Fast-forward only to avoid hard reset attacks Link to heading

Only checking the commit signature is not enough because an attacker could reset the repository to a previously signed commit (for instance, an older commit exposed to a CVE).

To avoid this attack, comin ensures the main branch repository has not been pushed forced to a previous commit: comin stores the last commit ID of the main branch and verifies this commit is a parent of all new fetched commits. If it is not, comin doesn’t checkout such kind of commits.

Testing branches Link to heading

We have seen comin refuses to fetch from the main branch if it has been hard reset. For the testing branch, this constraint has been relaxed. comin only ensures the testing branch is on top of the main branch. An attacker could hard reset the testing branch, but only to a commit having the current main commit as parent which limits the risk.

If you consider this risk is too important, you can still never push commits to testing branches or explicitly disable the testing branch feature.

Future works Link to heading

It would be nice to be able to configure signature checks per remotes and/or per branches.

Regarding the security model, there is still an issue when you bootstrap comin. The first time comin runs on a machine, it doesn’t know the latest main commit. So, it could fetch an already corrupted remote. To avoid this issue, it could be possible to add an option to specify the parent commit ID of the first fetched commit.

Conclusion Link to heading

comin can now ensure only authorized people are allowed to deploy configurations to NixOS machines. These people don’t need any access to the machines (no SSH required) and comin doesn’t rely on a secret (a public key is not a secret). I think these properties are pretty valuable for infrastructures managed by communities, because several people are publicly allowed to deploy configurations, without having an SSH access to the machines.