1 unstable release
0.1.0 | Sep 19, 2023 |
---|
#2035 in Cryptography
170KB
3K
SLoC
Sequoia git
sequoia-git
is tool that can be used to improve a project's supply
chain security.
Introduction
A version control system like git doesn't just track changes, it also provides a record of who made those changes. This information can be used to check that commits are authorized, which can improve software supply chain security. In particular, checking a change's provenance can be used to remove intermediaries like forges, and package registries from a user's trusted computing base. But, authorship information can easily be forged.
An obvious solution to prevent forgeries would be to require that commits are digitally signed. But by itself a valid digital signature doesn't prevent forgeries. The certificate that was used to make the signature could claim to be one of the project's maintainers. What is needed is not only a list of entities who are allowed to modify the repository, but also the keys they use to sign the commits. In other words, to authenticate a commit we need a signing policy, which says what keys are authorized to make changes.
Creating a policy isn't complicated. A project's maintainers could curate a list of entities who are allowed to add commits, and enumerate the certificates they use to sign them. The tricky part is applying the policy. There are a number of edge cases that need to be handled like how merge changes from external contributions, who is allowed to change the policy, and how to deal with compromised keys.
Sequoia git is a project that specifies a set of semantics, defines a policy language, and provides a set of tools to manage a policy file, and authenticate commits.
Using Sequoia git is relatively straightforward. You start by adding
a policy file, openpgp-policy.toml
, to your project's repository.
The policy is maintained in band to allow it to evolve, just like the
rest of the project. The openpgp-policy.toml
file is basically a
list of OpenPGP certificates and the type of changes they are
authorized to make. sq-git
can help you create it.
Then, before you merge a pull request, you check that commits are
authorized by the policy. Locally, this is done by running sq-git log
on the range of commits that you want to push. A commit is
considered authorized if the commit has a valid signature, and at
least one immediate parent's policy allows the signer to make that
type of change. Projects hosted on GitHub can use this action to
automatically check that a pull request is authorized when it is
opened, or updated.
Downstream users can use Sequoia git to check that there is a chain of trust from an older, known-good version of the software to a new version. This helps prevent the use of versions that include modifications that weren't authorized by the project's maintainers.
See the specification for an in-depth discussion of semantics and implementation.
Deploying sq-git
To start using Sequoia git in a git repository, you first add one or more certificates to the project's policy, and grant them some rights.
The policy is called openpgp-policy.toml
, and is stored in the root
of the repository. It is a toml file, which means
it can be edited by hand, but sq-git
provides tools that make it
easier to examine and modify it.
There are six different rights: add-user
, retire-user
, audit
,
sign-tag
, sign-archive
, and sign-commit
. Only users who have
the add-user
right can add new users to the policy. Similarly, the
retire-user
right is needed to remove users from the policy. The
audit
right is needed to good list (using sq-git policy goodlist
)
a commit that was signed by a certificate that was subsequently hard
revoked. The sign-tag
, sign-archive
, and sign-commit
rights are
needed to sign tags, archives, and commits, respectively.
You can use sq-git policy authorize
to grant a specific right to a
user. For instance, you could run:
$ sq-git policy authorize --sign-commit 'Neal H. Walfield <neal@pep.foundation>' F7173B3C7C685CD9ECC4191B74E445BA0E15C957
This says that the specified certificate can be used to sign commits. The name is purely decorative.
To make assigning rights easier, sq-git policy authorize
knows about
three roles: the project maintainer (who gets all rights), the release
manager (who can sign tags, archives, and commits), and the committer
(who can only sign commits). These can be passed to sq-git policy authorized
. For instance:
$ sq-git policy authorize --project-maintainer 'Neal H. Walfield <neal@pep.foundation>' F7173B3C7C685CD9ECC4191B74E445BA0E15C957
sq-git policy authorize
immediately expands the roles to the
corresponding rights; the roles do not appear in the policy file.
You can use the sq-git init
subcommand to get a quick overview of
who has contributed to the project, and what certificates they used to
sign their commits, if any. sq-git init
looks at commits from the
last half year, or the last 10 commits, whichever is more. As such it
focuses on contributors who have been active recently; it doesn't make
sense to authorize someone has left the project.
Although sq-git init
usually provides a good starting point, you
should not trust it. It is essential to verify a contributor's
certificate by, e.g., reaching out to them, and asking what their
certificate's fingerprint is. As you can always modify the policy
later, it is better to only add the certificates that you are certain
about, from contributors who are active.
Here's how you might initialize a policy file:
../sequoia-git$ sq-git init
# Examined the 136 commits in the last 183 days.
# Stopped at commit 83ce12f617c9e1dd90f812825707337f8787f69e.
# Encountered 0 unsigned commits
# Neal H. Walfield <neal@walfield.org> added 66 commits (48%).
#
# After checking that they really control the following OpenPGP keys:
#
# 6863C9AD5B4D22D3 (66 commits)
#
# You can make them a project maintainer (someone who can add and
# remove committers) by running:
sq-git policy authorize --project-maintainer "Neal H. Walfield <neal@walfield.org>" 6863C9AD5B4D22D3
# Justus Winter <justus@sequoia-pgp.org> added 44 commits (32%).
#
# After checking that they really control the following OpenPGP keys:
#
# 686F55B4AB2B3386 (44 commits)
#
# You can make them a committer by running:
sq-git policy authorize --committer "Justus Winter <justus@sequoia-pgp.org>" 686F55B4AB2B3386
...
../sequoia-git$ sq-git policy authorize --project-maintainer "Neal H. Walfield <neal@walfield.org>" 6863C9AD5B4D22D3
- User "Neal H. Walfield <neal@walfield.org>" was added.
- User "Neal H. Walfield <neal@walfield.org>" was granted the right sign-commit.
- User "Neal H. Walfield <neal@walfield.org>" was granted the right sign-tag.
- User "Neal H. Walfield <neal@walfield.org>" was granted the right sign-archive.
- User "Neal H. Walfield <neal@walfield.org>" was granted the right add-user.
- User "Neal H. Walfield <neal@walfield.org>" was granted the right retire-user.
- User "Neal H. Walfield <neal@walfield.org>" was granted the right audit.
../sequoia-git$ sq-git policy authorize --committer "Justus Winter <justus@sequoia-pgp.org>" 686F55B4AB2B3386
- User "Justus Winter <justus@sequoia-pgp.org>" was added.
- User "Justus Winter <justus@sequoia-pgp.org>" was granted the right sign-commit.
sq-git
reads the certificates from the user's certificate store.
Use sq import < FILE
to import certificates in a file, sq keyserver get FINGERPRINT
to fetch certificates from a key server, etc.
Alternatively, you can provide the certificate to sq-git policy authorize
using the --cert-file
argument.
The policy file can be viewed as follows:
$ sq-git policy describe
# OpenPGP policy file for git, version 0
## Commit Goodlist
## Authorizations
0. Justus Winter <justus@sequoia-pgp.org>
- may sign commits
- has OpenPGP cert: D2F2C5D45BE9FDE6A4EE0AAF31855247603831FD
1. Neal H. Walfield <neal@walfield.org>
- may sign commits
- may sign tags
- may sign archives
- may add users
- may retire users
- may goodlist commits
- has OpenPGP cert: F7173B3C7C685CD9ECC4191B74E445BA0E15C957
If you are happy, you can add it to your git
repository in the usual
manner.
Don't forget to tell git
to sign commits by adding something like
the following to your repository's .git/config
file:
[user]
signingkey = F7173B3C7C685CD9ECC4191B74E445BA0E15C957
email = 'neal@pep.foundation'
name = 'Neal H. Walfield'
[commit]
gpgsign = true
Then run:
../sequoia-git$ git add openpgp-policy.toml
../sequoia-git$ git commit -m 'Add a commit policy.'
[main 911c4eb] Add a commit policy.
1 file changed, 119 insertions(+), 1831 deletions(-)
rewrite openpgp-policy.toml (94%)
Create a new commit, and verify the new version:
../sequoia-git$ echo 'hello world' > greeting
../sequoia-git$ git add greeting
../sequoia-git$ git commit -m 'Say hello.'
[main 698876a] Say hello.
1 file changed, 1 insertion(+)
create mode 100644 greeting
../sequoia-git$ sq-git log --trust-root 911c4eb1e9832d6df8e733bf103ca4c9f4637eb9
911c4eb1e9832d6df8e733bf103ca4c9f4637eb9..698876a7ff11fff2f8cd0df55bbe8fc5c5d224d9: Neal H. Walfield <neal@walfield.org> [74E445BA0E15C957]
Instead of entering the trust root manually, which is error prone, you can set the trust root in the repository's git config file:
../sequoia-git$ git config sequoia.trust-root 911c4eb1e9832d6df8e733bf103ca4c9f4637eb9
../sequoia-git$ sq-git log
911c4eb1e9832d6df8e733bf103ca4c9f4637eb9..698876a7ff11fff2f8cd0df55bbe8fc5c5d224d9: Cached positive verification
You can also use tags or branches, however, you must be careful as
these may be updated when you fetch from a remote repository using,
e.g., git fetch
.
Rejecting Unauthorized Commits
Insert the following line into hooks/update
on a shared git
server
to make it enforce the policy embedded in the repository starting at
the given trust root (<COMMIT>
), which is specified as a hash:
sq-git update-hook --trust-root=<COMMIT> "$@"
Using sq-git
in CI
sequoia-git
is available in an OCI image for ease of use inside of
CI pipelines.
Gitlab
To authenticate commits from a Gitlab CI pipeline, there is a script
included at scripts/gitlab.sh
which may be run as a job inside a
project's .gitlab-ci.yml
manifest:
authenticate-commits:
stage: test
image: registry.gitlab.com/sequoia-pgp/sequoia-git:latest
script:
- sq-git policy describe
- ./scripts/gitlab.sh
rules:
# TODO: We currently only authenticate the changes on non-merged
# branches where we use the default branch as the trust root. For
# the default branch, the project needs to set an explicit trust
# root.
- if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
GitHub
To use sq-git
to authenticate a pull request in GitHub, you can use
the sequoia-pgp/authenticate-commits
Action. This
action checks that the commits are authorized by the last commit of
the merge base. This
video shows a
demonstration of the action.
Note: GitHub's interface for merging pull requests offers three merge
strategies, but unfortunately none of them are appropriate for use
with Sequoia git, because they all modify the commits. With Sequoia
git, it is necessary to either rebase and fast forward the change, or
to add a signed merge commit. It is possible to use the
sequoia-pgp/fast-forward
action to fast forward
pull requests. When enabled for a repository, an authorized user can
add a comment containing /fast-forward
to the pull request, and the
action will fast forward the merge base.
Dependencies
~72MB
~1.5M SLoC