guix-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

branch master updated: website: Add draft of a post about commit authent


From: Ludovic Courtčs
Subject: branch master updated: website: Add draft of a post about commit authentication & all.
Date: Mon, 29 Jun 2020 16:35:12 -0400

This is an automated email from the git hooks/post-receive script.

civodul pushed a commit to branch master
in repository guix-artwork.

The following commit(s) were added to refs/heads/master by this push:
     new 93f11d6  website: Add draft of a post about commit authentication & 
all.
93f11d6 is described below

commit 93f11d6206059470d52e8c9b3d8feda5e21013b1
Author: Ludovic Courtès <ludo@gnu.org>
AuthorDate: Mon Jun 29 22:34:05 2020 +0200

    website: Add draft of a post about commit authentication & all.
    
    * website/drafts/commit-authentication.md,
    website/static/blog/img/commit-graph.dot: New files.
---
 website/drafts/commit-authentication.md  | 291 +++++++++++++++++++++++++++++++
 website/static/blog/img/commit-graph.dot |  16 ++
 2 files changed, 307 insertions(+)

diff --git a/website/drafts/commit-authentication.md 
b/website/drafts/commit-authentication.md
new file mode 100644
index 0000000..938e65c
--- /dev/null
+++ b/website/drafts/commit-authentication.md
@@ -0,0 +1,291 @@
+title: DRAFT Authenticated channels
+author: Ludovic Courtès
+---
+
+Software deployment tools like Guix are in a key position when it comes
+to securing the “software supply chain”—taking source code fresh from
+repositories and providing users with ready-to-use binaries.  We have
+been paying attention to several aspects of this problem in Guix:
+[authentication of pre-built
+binaries](https://guix.gnu.org/manual/en/html_node/Substitute-Authentication.html),
+[reproducible
+builds](https://guix.gnu.org/blog/tags/reproducible-builds/),
+[bootstrapping](https://guix.gnu.org/blog/tags/bootstrapping/), and
+[fast security
+updates](https://guix.gnu.org/blog/2016/timely-delivery-of-security-updates/).
+
+A couple of weeks ago, we addressed the elephant in the room:
+authentication of Guix code itself by [`guix
+pull`](https://guix.gnu.org/manual/en/html_node/Invoking-guix-pull.html),
+the tool that updates Guix and its package collection.  This article
+looks at what set out to address, how we achieved it, and how it
+compares to existing work in this area.
+
+# Securing updates
+
+The problem of securing distro updates is often viewed through the lens
+of binary distributions such as Debian, where the main asset to be
+protected are binaries themselves.  The functional deployment model that
+Guix and Nix implement is very different: conceptually, Guix is a
+_source distribution_, like Gentoo if you will.
+
+Pre-built binaries are of course available and very useful, but they’re
+optional; we can them
+[_substitutes_](https://guix.gnu.org/manual/en/html_node/Substitutes.html)
+because they’re just that: substitutes for local builds.  When you do
+choose to accept substitutes, they must be signed by one of the keys you
+authorized (this has been the case since [version 0.6 in
+2014](https://guix.gnu.org/blog/2014/gnu-guix-06-released/)).
+
+Guix consists of source code for the tools as well as all the [package
+definitions](https://guix.gnu.org/manual/en/html_node/Defining-Packages.html)—the
+_distro_.  When users run `guix pull`, what happens behind the scene is
+equivalent to `git clone` or `git pull`.  There are many ways this can
+go wrong.  An attacker can trick the user into pulling code from an
+alternate repository that contains malicious code or definitions for
+backdoored packages.  This is made more difficult by the fact that code
+is fetched over HTTPS from
+[Savannah](https://git.savannah.gnu.org/cgit/guix.git/) by default, but
+not impossible (FIXME: xref).  If Savannah is compromised ([as happened
+in
+2010](https://www.fsf.org/blogs/sysadmin/savannah-and-www.gnu.org-downtime)),
+an attacker can push code to the Guix repository, which everyone would
+pull.  The change might even go unnoticed and remain in the repository
+forever.  An attacker with access to Savannah can also reset the main
+branch to an earlier revision, leading users to install outdated
+software with known vulunerabilities—a _downgrade attack_.  These are
+the kind of attacks we want to protect against.
+
+# Authenticating Git checkouts
+
+If we take a step back, the problem we’re trying to solve is not
+specific to Guix and to software deployment tools: it’s about
+_authenticating Git checkouts_.  By that, we mean that when `guix pull`
+obtains code from Git, it should be able to tell that all the commits it
+fetched were pushed by authorized developers of the project.  We’re
+really looking at individual commits, not tags, because users can choose
+to pool at arbitrary point in the commit history of Guix and Guix
+[channels](https://guix.gnu.org/manual/en/html_node/Channels.html).
+
+Checkout authentication requires [cryptographically signed
+commits](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work).
+By signing a commit, a Guix developer takes reponsibility asserts that
+they are the one who made the commit; they may be its author, or they
+may be the person who applied somebody else’s changes after review.  It
+also requires a notion of authorization: we don’t simply want commits to
+have a valid signature, we want them to be signed by an authorized key.
+The set of authorized keys changes over time as people join and leave
+the project.
+
+To implement that, we came up with the following mechanism and rule:
+
+  1. The repository contains a [`.guix-authorizations`
+     
file](https://git.savannah.gnu.org/cgit/guix.git/tree/.guix-authorizations)
+     that lists the OpenPGP key fingerprints of authorized committers.
+  2. A commit is considered authentic if and only if it is signed by one
+     of the keys listed in the `.guix-authorizations` file of each of
+     its parent(s).  This is the _authorization invariant_.
+
+(Remember that Git commits form a directed acyclic graph (DAG) where
+each commit can have zero or more parents; merge commits have two parent
+commits, for instance.  Do not miss [_Git for Computer
+Scientists_](https://eagain.net/articles/git-for-computer-scientists/)
+for a pedagogical overview!)
+
+Let’s take an example to illustrate.  In the figure below, each box is a
+commit, and each arrow is a parent relationship:
+
+![Example commit graph.](https://guix.gnu.org/static/blog/img/commit-graph.svg)
+
+This figure shows two lines of development: the orange line may be the
+main development branch, while the purple line may correspond to a
+feature branch that was eventually merged in commit _F_.  _F_ is a merge
+commit, so it has two parents: _D_ and _E_.
+
+Labels next to boxes show who’s in `.guix-authorizations`: for commit A,
+only Alice is an authorized committer, and for all the other commits,
+both Bob and Alice are authorized committers.  For each commit, we see
+that the authorization invariant holds; for example:
+
+  - commit _B_ was made by Alice, who was the only authorized committer
+    in its parent, commit _A_;
+  - commit _C_ was made by Bob, who was among the authorized committers
+    as of commit _B_;
+  - commit _F_ was made by Alice, who was among the authorized
+    committers of both parents, commits _D_ and _E_.
+
+The authorization invariant has the nice property that it’s simple to
+state, and it’s simple to check and enforce.  This is what `guix pull`
+implements.  If your current Guix, [as returned by `guix
+describe`](https://guix.gnu.org/manual/en/html_node/Invoking-guix-describe.html),
+is at commit _A_ and you want to pull to commit _F_, `guix pull`
+traverses all these commits and checks the authorization invariant.
+
+Once a commit has been authenticated, all the commits in its [transitive
+closure](https://en.wikipedia.org/wiki/Transitive_closure#In_graph_theory)
+are known to be already authenticated.  `guix pull` keeps a local cache
+of the commits it has previously authenticated, which allows it to
+traverse only new commits.  For instance, if you’re at commit _F_ and
+later update to a descendant of _F_, authentication starts at _F_.
+
+The authorization invariant satisfies our needs to Guix.  It has one
+downside: it prevents pull-request-style workflows.  Indeed, merging the
+branch of a contributor not listed in `.guix-authorizations` would break
+the authorization invariant.  It’s a good tradeoff for Guix because our
+workflow relies on [patches carved into stone
+tablets](https://lwn.net/Articles/702177/) ([patch
+tracker](https://issues.guix.gnu.org/)), but it’s not suitable for every
+project out there.
+
+# Bootstrapping
+
+The attentive reader may have noticed that something’s missing from the
+explanation above: what do we do about commit _A_ in the example above?
+Or, in other words, which commit do we pick as the first one where we
+can start verifying the authentication invariant?
+
+We solve this bootstrapping issue by defining _channel introductions_.
+Previously, one would identify a channel simply by its URL.  Now, when
+introducing a channel to users, one needs to provide an additional piece
+of information: the first commit where the authorization invariant
+holds, and the fingerprint of the OpenPGP key used to sign that commit
+(it’s not strictly necessary but provides an additional check).
+
+As always when it comes to establishing trust, distributing channel
+introductions is very sensitive.  The introduction of the official
+`guix` channel is [built into
+Guix](https://git.savannah.gnu.org/cgit/guix.git/tree/guix/channels.scm#n155).
+Users obtain it when they install Guix the first time; hopefully they
+verify the signature on the Guix tarball or ISO image, [as noted in the
+installation
+instructions](https://guix.gnu.org/manual/en/html_node/Binary-Installation.html),
+which reduces chances of getting the “wrong” Guix but is still very much
+[trust-on-first-use](https://en.wikipedia.org/wiki/Trust_on_first_use)
+(TOFU).
+
+For signed third-party channels, users have to provide the channel’s
+introduction in their `channels.scm` file, like so:
+
+```scheme
+(channel
+  (name 'my-channel)
+  (url "https://example.org/my-channel.git";)
+  (introduction
+   (make-channel-introduction
+    "6f0d8cc0d88abb59c324b2990bfee2876016bb86"
+    (openpgp-fingerprint
+     "CABB A931 C0FF EEC6 900D  0CFB 090B 1199 3D9A EBB5"))))
+```
+
+The `guix describe` command now prints the introduction if there’s one.
+That way, it’s easy for a user to share their channel configuration,
+including introductions, without having to be an expert
+
+Channel introductions also solve another problem: forks.  Respecting the
+authorization invariant “forever” would effectively prevent
+“unauthorized” forks (forks made by someone who’s not a committer).
+Someone willing to make a fork simply needs to emit a new introduction
+for their fork, pointing to a different starting commit.
+
+Last, channel introductions give a _point of reference_: if an attacker
+manipulates branch heads on Savannah to have them point to unrelated
+commits (such as commits on an orphan branch that do not share any
+history with the “official” branches), authentication will necessarily
+fail as it stumbles upon the first unauthorized commit made by the
+attacker.
+
+That’s all for authentication!  I’m glad you read this far.  At this
+point you can take a break or continue with the next section on how
+`guix pull` prevents downgrade attacks.
+
+# Downgrade attacks
+
+An important threat for software deployment tools is _downgrade_ or
+_roll-back_ attacks.  The attack consists in tricking users into
+installing older, known-vulnerable software packages, which in turn may
+offer new ways to break into their system.  This is not strictly related
+to the authentication issue we’ve been discussing, except that it’s
+another important issue in this area that we took the opportunity to
+address.
+
+Guix saves provenance info for itself: `guix describe` prints that
+information, essentially the Git commits of the channels used during
+`git pull`:
+
+```
+$ guix describe
+Generation 149  Jun 17 2020 20:00:14    (current)
+  guix 8b1f7c0
+    repository URL: https://git.savannah.gnu.org/git/guix.git
+    branch: master
+    commit: 8b1f7c03d239ca703b56f2a6e5f228c79bc1857e
+```
+
+Thus, `guix pull`, once it has retrieved the latest commit of the
+selected branch, can verify that it is doing a _fast-forward update_ in
+Git parlance—just like `git pull` does, but compared to the
+previously-deployed Guix.  A fast-forward update is when the new commit
+is a descendant of the current commit.  Going back to the figure above,
+going from commit _A_ to commit _F_ is a fast-forward update, but going
+from _F_ to _A_ or from _C_ to _E_ is not.
+
+Not doing a fast-forward update would mean that the user is deploying an
+older version of the Guix currently used, or deploying an unrelated
+version from another branch.  In both cases, the user is at risk of
+ending up installing older, vulnerable software.
+
+By default `guix pull` now errors out on non-fast-forward updates,
+thereby protecting from roll-backs.  Users [who know what they’re
+doing](https://issues.guix.gnu.org/41882) can override that by passing
+`--allow-downgrades`.
+
+Authentication and roll-back prevention allow users to safely refer to
+mirrors of the Git repository.  If `git.savannah.gnu.org` is down, one
+can still update by fetching from a mirror, for instance with:
+
+```
+guix pull --url=https://github.com/guix-mirror/guix
+```
+
+If the repository at this URL is behind what the user already deployed,
+or if it’s not a genuine mirror, `guix pull` will abort.  In other
+cases, it will proceed.
+
+Unfortunately, there is no way to answer the general question “_is_ X
+_the latest commit of branch_ B_?_”.  Rollback detection prevents just
+that, rollbacks, but there’s no mechanism in place to tell whether a
+given mirror is stale.  To mitigate that, channel authors can specify,
+in the repository, the channel’s _primary URL_.  This piece of
+information lives in the `.guix-channel` file, in the repository, so
+it’s authenticated.  `guix pull` uses it to print a warning when the
+user pulls from a mirror:
+
+```
+$ guix pull --url=https://github.com/guix-mirror/guix
+Updating channel 'guix' from Git repository at 
'https://github.com/guix-mirror/guix'...
+Authenticating channel 'guix', commits 9edb3f6 to 3e51f9e (44 new commits)...
+guix pull: warning: pulled channel 'guix' from a mirror of 
https://git.savannah.gnu.org/git/guix.git, which might be stale
+Building from this channel:
+  guix      https://github.com/guix-mirror/guix 3e51f9e
+…
+```
+
+So far we talked about mechanics in a rather abstract way.  That might
+satisfy the graph theorist or the Git geek in you, but if you’re up for
+a quick tour of the implementation, the next section is for you!
+
+# A long process
+
+  - start signing commits
+  - add "make authenticate"
+  - implement .guix-authorizations
+  - generalize to channels
+  - prevent downgrades
+  - add primary URL
+  - third-party channels
+
+# SHA1
+# Related work
+# Future work
+
+  - 'guix channel add'
diff --git a/website/static/blog/img/commit-graph.dot 
b/website/static/blog/img/commit-graph.dot
new file mode 100644
index 0000000..7547ec3
--- /dev/null
+++ b/website/static/blog/img/commit-graph.dot
@@ -0,0 +1,16 @@
+digraph "Grafts" {
+  ratio = .4;
+  A [ label = "A\nauthor: Alice", shape = box, fontname = Helvetica, xlabel = 
"authorized: Alice", color="#aa4422" ];
+  B [ label = "B\nauthor: Alice", shape = box, fontname = Helvetica, xlabel = 
"authorized: Alice, Bob", color="#aa4422"  ];
+  C [ label = "C\nauthor: Bob", shape = box, fontname = Helvetica, xlabel = 
"authorized: Alice, Bob", color="#22aa44" ];
+  D [ label = "D\nauthor: Alice", shape = box, fontname = Helvetica, xlabel = 
"authorized: Alice, Bob", color="#aa4422"  ];
+  E [ label = "E\nauthor: Bob", shape = box, fontname = Helvetica, xlabel = 
"authorized: Alice, Bob", color="#22aa44" ];
+  F [ label = "F\nauthor: Alice", shape = box, fontname = Helvetica, xlabel = 
"authorized: Alice, Bob", color="#aa4422"  ];
+
+  B -> A [ color = orange ];
+  C -> B [ color = orange ];
+  D -> C [ color = orange ];
+  F -> D [ color = orange ];
+  F -> E [ color = darkviolet ];
+  E -> B [ color = darkviolet ];
+}
\ No newline at end of file



reply via email to

[Prev in Thread] Current Thread [Next in Thread]