|
|
|
@ -0,0 +1,724 @@
|
|
|
|
|
(use-modules (skribilo package acmproc)
|
|
|
|
|
(skribilo engine)
|
|
|
|
|
(skribilo engine latex)
|
|
|
|
|
(skribilo ast)
|
|
|
|
|
(skribilo writer)
|
|
|
|
|
(skribilo output)
|
|
|
|
|
(skribilo utils strings)
|
|
|
|
|
(skribilo lib)
|
|
|
|
|
(skribilo evaluator)
|
|
|
|
|
(skribilo biblio author)
|
|
|
|
|
(skribilo source)
|
|
|
|
|
(skribilo source lisp)
|
|
|
|
|
(skribilo source parameters))
|
|
|
|
|
|
|
|
|
|
(define (---) ; emdash
|
|
|
|
|
(resolve (lambda (n e env)
|
|
|
|
|
(if (engine-format? "html" e)
|
|
|
|
|
(! "—")
|
|
|
|
|
(! "---")))))
|
|
|
|
|
|
|
|
|
|
(define (--) ; endash
|
|
|
|
|
(resolve (lambda (n e env)
|
|
|
|
|
(if (engine-format? "html" e)
|
|
|
|
|
(! "–")
|
|
|
|
|
(! "--")))))
|
|
|
|
|
|
|
|
|
|
(define (dash-dash)
|
|
|
|
|
(resolve (lambda (n e env)
|
|
|
|
|
(if (engine-format? "latex" e)
|
|
|
|
|
(! "{-}{-}")
|
|
|
|
|
"--"))))
|
|
|
|
|
|
|
|
|
|
(define (url url)
|
|
|
|
|
(ref :text (tt url) :url url))
|
|
|
|
|
|
|
|
|
|
(define (=>)
|
|
|
|
|
(symbol "=>"))
|
|
|
|
|
|
|
|
|
|
;; XXX: Terrible hack to turn hyphens into hyphenation points in 'tt'.
|
|
|
|
|
(set! (@@ (skribilo engine latex) latex-tt-encoding)
|
|
|
|
|
`((#\- "-\\-")
|
|
|
|
|
,@(@@ (skribilo engine latex) latex-tt-encoding)))
|
|
|
|
|
|
|
|
|
|
;; For pdflatex.
|
|
|
|
|
(engine-custom-set! (find-engine 'latex) 'image-format '("pdf"))
|
|
|
|
|
|
|
|
|
|
;; Avoid "option clash" with acmart.
|
|
|
|
|
(engine-custom-set! (find-engine 'latex) 'hyperref #f)
|
|
|
|
|
|
|
|
|
|
(engine-custom-set! (find-engine 'latex) 'usepackage
|
|
|
|
|
(let ((u (engine-custom (find-engine 'latex)
|
|
|
|
|
'usepackage)))
|
|
|
|
|
;; See <https://en.wikibooks.org/wiki/LaTeX/Labels_and_Cross-referencing>
|
|
|
|
|
;; and <http://tug.org/pipermail/texhax/2010-September/015596.html>.
|
|
|
|
|
(string-append u "\n"
|
|
|
|
|
"\\usepackage{microtype}\n"
|
|
|
|
|
;; "\\usepackage[hypcap]{caption}\n"
|
|
|
|
|
;; "\\DeclareCaptionType{copyrightbox}\n"
|
|
|
|
|
"\\usepackage{balance}\n"
|
|
|
|
|
|
|
|
|
|
;; Trick so that ‘…’ is properly
|
|
|
|
|
;; typeset inside teletype text.
|
|
|
|
|
"\\DeclareUnicodeCharacter{2026}{\\textrm{\\ldots}}\n"
|
|
|
|
|
|
|
|
|
|
"\
|
|
|
|
|
\\fancyhf{} % Remove fancy page headers
|
|
|
|
|
\\fancyhead[C]{Anonymous submission \\#9999 to ACM CCS 2021} % TODO: replace 9999 with your paper number
|
|
|
|
|
\\fancyfoot[C]{\\thepage}
|
|
|
|
|
|
|
|
|
|
\\setcopyright{none} % No copyright notice required for submissions
|
|
|
|
|
\\acmConference[Anonymous Submission to ACM CCS 2021]{ACM Conference on Computer and Communications Security}{Due 15 May 2021}{Seoul}
|
|
|
|
|
\\acmYear{2021}
|
|
|
|
|
|
|
|
|
|
\\settopmatter{printacmref=false, printccs=true, printfolios=true} % We want page numbers on submissions
|
|
|
|
|
|
|
|
|
|
%%\\ccsPaper{9999} % TODO: replace with your paper number once obtained
|
|
|
|
|
|
|
|
|
|
\\let\\oldthing\\footnotetextcopyrightpermission
|
|
|
|
|
\\renewcommand\\footnotetextcopyrightpermission[1]{\\oldthing{
|
|
|
|
|
% Per
|
|
|
|
|
% <https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006069414&idArticle=LEGIARTI000006278959&dateTexte=20170831>,
|
|
|
|
|
% the copyright holder is me personally, not Inria.
|
|
|
|
|
Copyright \\copyright 2021 Ludovic Courtès.\\
|
|
|
|
|
|
|
|
|
|
Permission is granted to copy, distribute and/or modify this document
|
|
|
|
|
under the terms of the GNU Free Documentation License, Version 1.3
|
|
|
|
|
or any later version published by the Free Software Foundation;
|
|
|
|
|
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
|
|
|
|
A copy of the license is
|
|
|
|
|
available at \\url{https://www.gnu.org/licenses/gfdl.html}.
|
|
|
|
|
|
|
|
|
|
% Give a link to the 'Transparent Copy', as per Section 3 of the GFDL.
|
|
|
|
|
The source of this document is available from
|
|
|
|
|
\\url{https://git.sv.gnu.org/cgit/guix/maintenance.git}.
|
|
|
|
|
}}
|
|
|
|
|
")))
|
|
|
|
|
|
|
|
|
|
(let ((latex (find-engine 'latex)))
|
|
|
|
|
(engine-custom-set! latex 'documentclass
|
|
|
|
|
"\\documentclass[sigplan]{acmart}")
|
|
|
|
|
|
|
|
|
|
(engine-custom-set! latex 'maketitle #f)
|
|
|
|
|
|
|
|
|
|
(markup-writer '&latex-author latex
|
|
|
|
|
:action (lambda (n e)
|
|
|
|
|
(let ((body (markup-body n)))
|
|
|
|
|
(for-each (lambda (a)
|
|
|
|
|
(display "\\author{")
|
|
|
|
|
(output (markup-option a :name) e)
|
|
|
|
|
(display "}\n\\affiliation{\n")
|
|
|
|
|
(display " \\institution{")
|
|
|
|
|
(output (markup-option a :affiliation) e)
|
|
|
|
|
(display "}\n \\city{")
|
|
|
|
|
(output (markup-option a :address) e)
|
|
|
|
|
(display "}}\n"))
|
|
|
|
|
(if (pair? body) body (list body))))))
|
|
|
|
|
|
|
|
|
|
(markup-writer '&acm-category latex
|
|
|
|
|
:options '(:index :section :subsection)
|
|
|
|
|
:action (lambda (n e)
|
|
|
|
|
(display "\\ccsdesc[")
|
|
|
|
|
(display (markup-option n :index))
|
|
|
|
|
(display "]")
|
|
|
|
|
(display "{")
|
|
|
|
|
(display (markup-option n :section))
|
|
|
|
|
(display "}\n")))
|
|
|
|
|
|
|
|
|
|
(markup-writer 'image latex
|
|
|
|
|
:options '(:file :url :width :height :zoom)
|
|
|
|
|
:action (lambda (n e)
|
|
|
|
|
(format #t "\n\\includegraphics[width=~a\\textwidth]{~a}\n"
|
|
|
|
|
(or (markup-option n :width) 0.5)
|
|
|
|
|
(markup-option n :file))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(markup-writer 'prog latex
|
|
|
|
|
:class "small"
|
|
|
|
|
:options '(:line :mark)
|
|
|
|
|
:before "\n\n\n\\begin{scriptsize}\n"
|
|
|
|
|
:action (lambda (n e)
|
|
|
|
|
;; Delegate actual work to the "real" 'prog'.
|
|
|
|
|
(output (prog :line (markup-option n :line)
|
|
|
|
|
:mark (markup-option n :mark)
|
|
|
|
|
(node-body n))
|
|
|
|
|
e))
|
|
|
|
|
:after "\n\\end{scriptsize}\n")
|
|
|
|
|
|
|
|
|
|
(markup-writer 'figure latex
|
|
|
|
|
:options '(:legend :number :multicolumns)
|
|
|
|
|
:action (lambda (n e)
|
|
|
|
|
(let ((ident (markup-ident n))
|
|
|
|
|
(legend (markup-option n :legend))
|
|
|
|
|
(mc (markup-option n :multicolumns)))
|
|
|
|
|
(display "\\begin{figure}[ht]\n\\begin{scriptsize}\n")
|
|
|
|
|
(output (markup-body n) e)
|
|
|
|
|
(display "\n\\end{scriptsize}\n")
|
|
|
|
|
(format #t "\\caption{\\label{~a}"
|
|
|
|
|
(string-canonicalize ident))
|
|
|
|
|
(output legend e)
|
|
|
|
|
(display "}\\end{figure}\n")))))
|
|
|
|
|
|
|
|
|
|
(define (acmart-abstract . body)
|
|
|
|
|
(!latex "\\begin{abstract}\n$1\n\\end{abstract}\n" body))
|
|
|
|
|
|
|
|
|
|
(bibliography "../els-2013/guix.sbib")
|
|
|
|
|
(bibliography "../reppar-2015/reppar.sbib")
|
|
|
|
|
(bibliography "security.sbib")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(document :title [Secure Software Supply Chain with GNU Guix]
|
|
|
|
|
;; :author (list (author :name "Ludovic Courtès"
|
|
|
|
|
;; :affiliation "Inria"
|
|
|
|
|
;; :address (list "Bordeaux, France")))
|
|
|
|
|
|
|
|
|
|
(acmart-abstract
|
|
|
|
|
|
|
|
|
|
(p [GNU Guix blah blah FIXME.]))
|
|
|
|
|
|
|
|
|
|
;; See <http://dl.acm.org/ccs/ccs_flat.cfm>.
|
|
|
|
|
(!latex "\\input{categories.tex}\n")
|
|
|
|
|
|
|
|
|
|
(acm-keywords [Software deployment, Git, ])
|
|
|
|
|
|
|
|
|
|
(!latex "\\maketitle\n")
|
|
|
|
|
|
|
|
|
|
(chapter :title [Introduction]
|
|
|
|
|
|
|
|
|
|
(p [Package managers and related software deployment tools are in
|
|
|
|
|
a key position when it comes to securing the “software supply
|
|
|
|
|
chain”—they take source code fresh from repositories and providing users
|
|
|
|
|
with ready-to-use binaries. Between source code repositories and
|
|
|
|
|
binaries users run, many things can go wrong: binaries can be
|
|
|
|
|
compromised on their way to the user's machine, on the provider's
|
|
|
|
|
servers, or possibly indirectly ,(it [via]) toolchain compromission
|
|
|
|
|
,(ref :bib "thompson1984:trusting-trust"). Every software installation
|
|
|
|
|
and every upgrade can put users at risk.])
|
|
|
|
|
|
|
|
|
|
(p [GNU Guix is a set of software deployment tools and a
|
|
|
|
|
standalone GNU/Linux distribution; it includes a package manager similar
|
|
|
|
|
in spirit to Debian’s apt or Fedora’s yum. Unlike those, Guix builds
|
|
|
|
|
upon the ,(emph [functional deployment model]) pioneered by Nix,(ref
|
|
|
|
|
:bib "dolstra2004:nix"), a foundation for reproducible deployment,
|
|
|
|
|
reproducible builds, and provenance tracking. Guix is essentially a
|
|
|
|
|
“source-based” deployment tools: the ,(emph [model]) is that of a system
|
|
|
|
|
where every piece of software is built from source, and pre-built
|
|
|
|
|
binaries are viewed as a mere optimization and not as a central aspect
|
|
|
|
|
of its design.])
|
|
|
|
|
|
|
|
|
|
(p [This paper describes the design and implementation of Guix’s
|
|
|
|
|
secure update mechanism. ,(numref :text [Section] :ident "background")
|
|
|
|
|
gives background information necessary to understand the overall
|
|
|
|
|
deployment model of Guix. FIXME: complete]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Background] :ident "background"
|
|
|
|
|
|
|
|
|
|
(p [Users of free operating systems such as GNU/Linux are used to
|
|
|
|
|
,(emph [package managers]) like Debian's ,(tt [apt]), which allow them
|
|
|
|
|
to install, upgrade, and remove software from a large collection of free
|
|
|
|
|
software packages. GNU Guix,(footnote (url "https://guix.gnu.org")) is
|
|
|
|
|
primarily a ,(emph [functional]) package manager that builds upon the
|
|
|
|
|
ideas developed for Nix by Dolstra ,(it [et al.]) ,(ref :bib
|
|
|
|
|
'(dolstra2004:nix courtes2013:functional)). The term “functional” means
|
|
|
|
|
that software build processes are considered as pure functions: given a
|
|
|
|
|
set of inputs (compiler, libraries, build scripts, and so on), a
|
|
|
|
|
package’s build function is assumed to always produce the same result.
|
|
|
|
|
Build results are stored in an immutable persistent data structure, the
|
|
|
|
|
,(emph [store]), implemented as a single directory, ,(tt [/gnu/store]).
|
|
|
|
|
Each entry in ,(tt [/gnu/store]) has a file name composed of the hash of
|
|
|
|
|
all the build inputs used to produce it, followed by a symbolic name.
|
|
|
|
|
For example, ,(tt [/gnu/store/yr9rk90jf…-gcc-10.3.0]) identifies a
|
|
|
|
|
specific build of GCC 10.3. A variant of GCC 10.3, for instance one
|
|
|
|
|
using different build options or different dependencies, would get a
|
|
|
|
|
different hash. Thus, each store file name uniquely identifies build
|
|
|
|
|
results, and build processes are ,(emph [referentially transparent]).])
|
|
|
|
|
(p [Guix, like Nix and unlike Debian or Fedora, is essentially a
|
|
|
|
|
,(emph [source-based distribution]): Guix package definitions describe
|
|
|
|
|
how to build packages from source. When running a command such as ,(tt
|
|
|
|
|
[guix install gcc]), Guix proceeds as if it were to build GCC from
|
|
|
|
|
source. As an optimization, users can enable fetching pre-built
|
|
|
|
|
binaries—called ,(emph [substitutes]) because they are substitutes for a
|
|
|
|
|
local build. In that case, instead of building locally, Guix asks one
|
|
|
|
|
or more servers for substitutes. In the example above, it would ask
|
|
|
|
|
specifically for substitutes for ,(tt
|
|
|
|
|
[/gnu/store/yr9rk90jf…-gcc-10.3.0]), which unambiguously identifies the
|
|
|
|
|
desired build output. Substitutes are cryptographically signed by the
|
|
|
|
|
server and Guix rejects substitutes not signed by one of the keys the
|
|
|
|
|
user authorized.])
|
|
|
|
|
(p [To maximize chances that build processes actually look like
|
|
|
|
|
pure functions, they are spawned in isolated build environments—Linux
|
|
|
|
|
,(emph [containers])—ensuring that only explicitly declared inputs are
|
|
|
|
|
visible to the build process. This, in turn, helps achieve bit-for-bit
|
|
|
|
|
,(emph [reproducible builds]), which are critical from a security
|
|
|
|
|
standpoint ,(ref :bib 'lamb2021:reproducible). Reproducible builds
|
|
|
|
|
enable users and developers to verify that a binary matches a given
|
|
|
|
|
piece of source code: anyone can rebuild the package and ensure they
|
|
|
|
|
obtain the same binary, bit for bit. The explicit and unambiguous
|
|
|
|
|
mapping from source to binary that the functional deployment model
|
|
|
|
|
provides makes verification clear and easy. For example, the command
|
|
|
|
|
,(tt [guix build --check hello]) rebuilds the ,(tt [hello]) package
|
|
|
|
|
locally and prints an error if the build result differs from that
|
|
|
|
|
already available. Likewise, ,(tt [guix challenge hello]) compares
|
|
|
|
|
binaries of the ,(tt [hello]) package available locally with those
|
|
|
|
|
provided by one or several substitute servers.])
|
|
|
|
|
(p [Are reproducible builds enough to guarantee that one can
|
|
|
|
|
verify source-to-binary mappings? In his Turing Award acceptance speech
|
|
|
|
|
,(ref :bib 'thompson1984:trusting-trust), Ken Thompson described a
|
|
|
|
|
scenario whereby a legitimate-looking build process would produce a
|
|
|
|
|
malicious binary—if that build process is reproducible, it just
|
|
|
|
|
reproducibly build a malicious binary. The attack Thompson described,
|
|
|
|
|
often referred to as a “Trusting Trust attack”, consists in targeting
|
|
|
|
|
the compilation toolchain, typically by modifying the compiler such that
|
|
|
|
|
it emits malicious code when it recognizes specific patterns of source
|
|
|
|
|
code. This attack can be undetectable. What makes such attacks
|
|
|
|
|
possible is that users and distributions rely on opaque binaries at some
|
|
|
|
|
level to “bootstrap” the entire package dependency graph.])
|
|
|
|
|
(p [In 2017, Jan Nieuwenhuizen ,(it [et al.]) sought to address
|
|
|
|
|
this forty-year-old problem at its root: by ensuring no opaque binaries
|
|
|
|
|
appear in the package dependency graph—no less ,(ref :bib
|
|
|
|
|
'janneke:mes-web). To that end, Nieuwenhuizen developed GNU Mes, a
|
|
|
|
|
small interpreter of the Scheme language written in C, capable enough to
|
|
|
|
|
run MesCC, a non-optimizing C compiler. That, coupled with other heroic
|
|
|
|
|
efforts, led to a drastic reduction of the size of the opaque binaries
|
|
|
|
|
at the root of the Guix package graph, well below what had been achieved
|
|
|
|
|
so far ,(ref :bib 'janneke2020:bootstrap). While many considered it
|
|
|
|
|
unrealistic a few years earlier, the initial goal of building ,(emph
|
|
|
|
|
[everything]) from source, starting from a small core and incrementally
|
|
|
|
|
building more complex pieces of software, is now within reach. This has
|
|
|
|
|
the potential to thwart an entire class of software supply chain attacks
|
|
|
|
|
that has been known but left unaddressed for forty years.]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Rationale] :ident "rationale"
|
|
|
|
|
|
|
|
|
|
(p [As we have seen, Guix is conceptually a source-based
|
|
|
|
|
distribution. It addresses common classes of software supply chain
|
|
|
|
|
attacks in two ways: by reducing and eventually removing reliance on
|
|
|
|
|
opaque binaries at the root of its dependency graph, and by affording
|
|
|
|
|
reproducible builds. Guix users can choose to obtain pre-built binaries
|
|
|
|
|
for software they install, and reproducible builds guarantee that anyone
|
|
|
|
|
can verify that provides of those binaries are not distributing modified
|
|
|
|
|
or malicious versions.])
|
|
|
|
|
|
|
|
|
|
(p [The security issue that the remainder of this paper focuses on
|
|
|
|
|
is that of ,(emph [distributing updates securely]): how can users know
|
|
|
|
|
that updates to Guix and its package collection that they fetch are
|
|
|
|
|
genuine? The problem of securing software updates is often viewed
|
|
|
|
|
through the lens of binary distributions such as Debian, where the main
|
|
|
|
|
asset to be protected are binaries themselves. Guix being a
|
|
|
|
|
source-based distribution, the question has to be approached from a
|
|
|
|
|
different angle.])
|
|
|
|
|
|
|
|
|
|
(p [Guix consists of source code for the tools as well as package
|
|
|
|
|
definitions that make up the GNU/Linux distribution. All this code is
|
|
|
|
|
maintained under version control in a Git repository. To update Guix
|
|
|
|
|
and its package collection, users run ,(tt [guix pull])—the equivalent
|
|
|
|
|
of ,(tt [apt update]) in Debian. When users run ,(tt [guix pull]), what
|
|
|
|
|
happens behind the scene is equivalent to ,(tt [git clone]) or ,(tt [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
|
|
|
|
|
by default. If Savannah is compromised as happened in 2010,(footnote
|
|
|
|
|
[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 vulnerabilities—a ,(emph [downgrade attack]). These
|
|
|
|
|
are the kind of attacks we want to protect against.]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Authenticating Git checkouts]
|
|
|
|
|
:ident "authenticating"
|
|
|
|
|
|
|
|
|
|
(p [If we take a step back, the problem we are trying to solve is
|
|
|
|
|
not specific to Guix and to software deployment tools: it’s about
|
|
|
|
|
,(emph [authenticating Git checkouts]). By that, we mean that when ,(tt [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 are
|
|
|
|
|
really looking at individual commits, not tags, because users can choose
|
|
|
|
|
to pull arbitrary points in the commit history of Guix and third-party
|
|
|
|
|
channels.])
|
|
|
|
|
|
|
|
|
|
(p [Checkout authentication requires cryptographically signed
|
|
|
|
|
commits,(footnote
|
|
|
|
|
[https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work]). By
|
|
|
|
|
signing a commit, a Guix developer 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.])
|
|
|
|
|
|
|
|
|
|
(p [To implement that, we came up with the following mechanism and rule:
|
|
|
|
|
|
|
|
|
|
,(enumerate
|
|
|
|
|
(item [The repository contains a ,(tt [.guix-authorizations])
|
|
|
|
|
file
|
|
|
|
|
that lists the OpenPGP key fingerprints of authorized committers.])
|
|
|
|
|
(item [A commit is considered authentic if and only if it is signed by one
|
|
|
|
|
of the keys listed in the ,(tt [.guix-authorizations]) file of each of
|
|
|
|
|
its parents. This is the ,(emph [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_,(footnote [https://eagain.net/articles/git-for-computer-scientists/])
|
|
|
|
|
for a pedagogical overview!)])
|
|
|
|
|
|
|
|
|
|
(figure
|
|
|
|
|
:legend [Graph of commits and the associated authorizations.]
|
|
|
|
|
:ident "fig-commits"
|
|
|
|
|
(image :file "images/commit-graph.pdf"))
|
|
|
|
|
|
|
|
|
|
(p [Let’s take an example to illustrate. In ,(numref :text
|
|
|
|
|
[Figure] :ident "fig-commits"), each box is a commit, and each arrow is
|
|
|
|
|
a parent relationship.])
|
|
|
|
|
|
|
|
|
|
(p [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 ,(it [F]). ,(it [F]) is a merge
|
|
|
|
|
commit, so it has two parents: ,(it [D]) and ,(it [E]).])
|
|
|
|
|
|
|
|
|
|
(p [Labels next to boxes show who’s in ,(tt [.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:
|
|
|
|
|
|
|
|
|
|
,(itemize
|
|
|
|
|
(item [commit ,(it [B]) was made by Alice, who was the only authorized committer
|
|
|
|
|
in its parent, commit ,(it [A]);])
|
|
|
|
|
(item [commit ,(it [C]) was made by Bob, who was among the authorized committers
|
|
|
|
|
as of commit ,(it [B]);])
|
|
|
|
|
(item [commit ,(it [F]) was made by Alice, who was among the authorized
|
|
|
|
|
committers of both parents, commits ,(it [D]) and ,(it [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 ,(tt [guix pull])
|
|
|
|
|
implements. If your current Guix, as returned by ,(tt [guix describe]) is at
|
|
|
|
|
commit ,(it [A]) and you want to pull to commit ,(it [F]), ,(tt [guix pull]) traverses all
|
|
|
|
|
these commits and checks the authorization invariant.])
|
|
|
|
|
|
|
|
|
|
(p [Once a commit has been authenticated, all the commits in its
|
|
|
|
|
transitive closure are known to be already authenticated. ,(tt [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 are
|
|
|
|
|
at commit ,(it [F]) and later update to a descendant of ,(it [F]), authentication
|
|
|
|
|
starts at ,(it [F]).])
|
|
|
|
|
|
|
|
|
|
(p [Since ,(tt [.guix-authorizations]) is a regular file under version
|
|
|
|
|
control, granting or revoking commit authorization does not require
|
|
|
|
|
special support. In the example above, commit ,(it [B]) is an authorized
|
|
|
|
|
commit by Alice that adds Bob’s key to ,(tt [.guix-authorizations]).
|
|
|
|
|
Revocation is similar: any authorized committer can remove entries from
|
|
|
|
|
,(tt [.guix-authorizations]). Key rotation can be handled similarly: a
|
|
|
|
|
committer can remove their former key and add their new key in a single
|
|
|
|
|
commit, signed by the former key.])
|
|
|
|
|
|
|
|
|
|
(p [The authorization invariant satisfies our needs for Guix. It
|
|
|
|
|
has one downside: it prevents pull-request-style workflows. Indeed,
|
|
|
|
|
merging the branch of a contributor not listed in ,(tt [.guix-authorizations])
|
|
|
|
|
would break the authorization invariant. It’s a good tradeoff for Guix
|
|
|
|
|
because our workflow relies on patches carved into stone tablets
|
|
|
|
|
,(footnote [https://lwn.net/Articles/702177/]) (patch tracker,(footnote
|
|
|
|
|
[https://issues.guix.gnu.org/])), but it’s not suitable for every
|
|
|
|
|
project out there.]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Bootstrapping] :ident "bootstrapping"
|
|
|
|
|
|
|
|
|
|
(p [The attentive reader may have noticed that something’s missing
|
|
|
|
|
from the explanation above: what do we do about commit ,(it [A]) in the
|
|
|
|
|
example above? In other words, which commit do we pick as the first one
|
|
|
|
|
where we can start verifying the authorization invariant?])
|
|
|
|
|
|
|
|
|
|
(figure
|
|
|
|
|
:legend [The introductory commit in a graph.]
|
|
|
|
|
:ident "fig-commit-graph-intro"
|
|
|
|
|
(image :file "images/commit-graph-intro.pdf"))
|
|
|
|
|
|
|
|
|
|
(p [We solve this bootstrapping issue by defining ,(emph [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).])
|
|
|
|
|
|
|
|
|
|
(p [Consider the commit graph on ,(numref :text [Figure] :ident
|
|
|
|
|
"fig-commit-graph-intro"). On this figure, ,(it [B]) is the introduction commit. Its
|
|
|
|
|
ancestors, such as ,(it [A]) are considered authentic. To authenticate, ,(it [C]),
|
|
|
|
|
,(it [D]), ,(it [E]), and ,(it [F]), we check the authorization invariant.])
|
|
|
|
|
|
|
|
|
|
(p [As always when it comes to establishing trust, distributing
|
|
|
|
|
channel introductions is very sensitive. The introduction of the
|
|
|
|
|
official ,(tt [guix]) channel is built into Guix. 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,
|
|
|
|
|
which reduces chances of getting the “wrong” Guix, but it is still very
|
|
|
|
|
much trust-on-first-use (TOFU).])
|
|
|
|
|
|
|
|
|
|
(figure
|
|
|
|
|
:legend [Specification of a channel along with its ,(emph [introduction]).]
|
|
|
|
|
:ident "fig-channel-spec"
|
|
|
|
|
|
|
|
|
|
(prog :line #f
|
|
|
|
|
(source :language 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"))))])))
|
|
|
|
|
|
|
|
|
|
(p [Guix supports third-party channels providing extra software
|
|
|
|
|
packages. To use a third-party channel, one needs to add it to the ,(tt
|
|
|
|
|
[~/.config/guix/channels.scm]) configuration file, which contains a
|
|
|
|
|
declarative Scheme code snippet listing the desired channels. Authors
|
|
|
|
|
of third-party channels can also benefit from the channel authentication
|
|
|
|
|
mechanism: they need to sign commits, to include a ,(tt
|
|
|
|
|
[.guix-authorizations]) file and the list of relevant OpenPGP keys, and
|
|
|
|
|
to advertise the channel's introduction. Users then have to provide the
|
|
|
|
|
channel’s introduction in their ,(tt [channels.scm]) file, as shown in
|
|
|
|
|
,(numref :text [Figure] :ident "fig-channel-spec").])
|
|
|
|
|
|
|
|
|
|
(p [The ,(tt [guix describe]) command now prints the introduction if
|
|
|
|
|
there’s one. That way, one can share their channel configuration,
|
|
|
|
|
including introductions, without having to be an expert.])
|
|
|
|
|
|
|
|
|
|
(p [Channel introductions also solve another problem: forks.
|
|
|
|
|
Respecting the authorization invariant “forever” would effectively
|
|
|
|
|
prevent “unauthorized” forks—forks made by someone who’s not in
|
|
|
|
|
,(tt [.guix-authorizations]). Someone publishing a fork simply needs to emit
|
|
|
|
|
a new introduction for their fork, pointing to a different starting
|
|
|
|
|
commit.])
|
|
|
|
|
|
|
|
|
|
(p [Last, channel introductions give a ,(emph [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. In the figure above, the red branch with commits ,(it [G])
|
|
|
|
|
and ,(it [H]) cannot be authenticated because it starts from ,(it [A]), which lacks
|
|
|
|
|
,(tt [.guix-authorizations]) and thus fails the authorization invariant.])
|
|
|
|
|
|
|
|
|
|
(p [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 ,(tt [guix pull]) prevents downgrade attacks.]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Downgrade attacks] :ident "downgrade"
|
|
|
|
|
|
|
|
|
|
(p [An important threat for software deployment tools is
|
|
|
|
|
,(emph [downgrade]) or ,(emph [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.])
|
|
|
|
|
|
|
|
|
|
(p [Guix saves provenance info for itself: ,(tt [guix describe]) prints that
|
|
|
|
|
information, essentially the Git commits of the channels used during
|
|
|
|
|
,(tt [git pull]):]
|
|
|
|
|
|
|
|
|
|
(prog :class "small" :line #f [
|
|
|
|
|
$ 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, ,(tt [guix pull]), once it has retrieved the latest commit of the
|
|
|
|
|
selected branch, can verify that it is doing a ,(emph [fast-forward update]) in
|
|
|
|
|
Git parlance—just like ,(tt [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 ,(it [A]) to commit ,(it [F]) is a fast-forward update, but going
|
|
|
|
|
from ,(it [F]) to ,(it [A]) or from ,(it [D]) to ,(it [E]) is not.])
|
|
|
|
|
|
|
|
|
|
(p [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.])
|
|
|
|
|
|
|
|
|
|
(p [By default ,(tt [guix pull]) now errors out on non-fast-forward updates,
|
|
|
|
|
thereby protecting from roll-backs. Users who understand the
|
|
|
|
|
risks can override that by passing
|
|
|
|
|
,(tt [--allow-downgrades]).]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Mirrors and the risk of staleness]
|
|
|
|
|
:ident "mirrors"
|
|
|
|
|
|
|
|
|
|
(p [Authentication and roll-back prevention allow users to safely
|
|
|
|
|
refer to mirrors of the Git repository. If the official repository at
|
|
|
|
|
,(tt [git.savannah.gnu.org]) is down, one can still update by fetching
|
|
|
|
|
from a mirror, for instance with:]
|
|
|
|
|
|
|
|
|
|
(prog :line #f :class "small" [
|
|
|
|
|
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, ,(tt [guix pull]) will abort. In other
|
|
|
|
|
cases, it will proceed.])
|
|
|
|
|
|
|
|
|
|
(p [Unfortunately, there is no way to answer the general question
|
|
|
|
|
“,(it [is]) X ,(it [the latest commit of branch]) B ,(it [?])”.
|
|
|
|
|
Rollback detection prevents just that, rollbacks, but there is 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
|
|
|
|
|
,(emph [primary URL]). This piece of information lives in the
|
|
|
|
|
`.guix-channel` file, in the repository, so it’s authenticated. ,(tt
|
|
|
|
|
[guix pull]) uses it to print a warning when the user pulls from a
|
|
|
|
|
mirror:]
|
|
|
|
|
|
|
|
|
|
(prog :line #f :class "small" [
|
|
|
|
|
$ guix pull --url=https://github.com/guix-mirror/guix
|
|
|
|
|
Updating channel 'guix' from 'https://github.com/guix-mirror/guix'...
|
|
|
|
|
Authenticating channel 'guix', commits 9edb3f6 to 3e51f9e...
|
|
|
|
|
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
|
|
|
|
|
…
|
|
|
|
|
]))
|
|
|
|
|
|
|
|
|
|
(p [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 are up for
|
|
|
|
|
a quick tour of the implementation, the next section is for you!]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Implementation])
|
|
|
|
|
|
|
|
|
|
(chapter :title [SHA-1] :ident "sha1"
|
|
|
|
|
|
|
|
|
|
(p [We can’t really discuss Git commit signing without mentioning
|
|
|
|
|
SHA-1. The venerable crytographic hash function is approaching end of
|
|
|
|
|
life, as evidenced by recent breakthroughs ,(ref :bib
|
|
|
|
|
'(stevens2017:collision leurent2020:shambles)). Signing a Git commit
|
|
|
|
|
boils down to signing a SHA-1 hash, because all objects in the Git store
|
|
|
|
|
are identified by their SHA-1 hash.])
|
|
|
|
|
|
|
|
|
|
(p [Git now relies on a collision attack detection library ,(ref
|
|
|
|
|
:bib 'stevens2017:detection) to mitigate practical attacks.
|
|
|
|
|
Furthermore, the Git project is planning a hash function
|
|
|
|
|
transition,(footnote
|
|
|
|
|
[https://git-scm.com/docs/hash-function-transition/]) to address the
|
|
|
|
|
problem.])
|
|
|
|
|
|
|
|
|
|
(p [Some projects such as Bitcoin Core choose to not rely on SHA-1
|
|
|
|
|
at all. Instead, for the commits they sign, they include in the commit
|
|
|
|
|
log the SHA512 hash of the tree, which the verification scripts
|
|
|
|
|
check,(footnote
|
|
|
|
|
[https://github.com/bitcoin/bitcoin/tree/master/contrib/verify-commits]).
|
|
|
|
|
Computing a tree hash ,(emph [for each commit]) in Guix would probably be
|
|
|
|
|
prohibitively costly. For now, for lack of a better solution, we rely
|
|
|
|
|
on Git’s collision attack detection and look forward to a hash function
|
|
|
|
|
transition.])
|
|
|
|
|
|
|
|
|
|
(p [As for SHA-1 in an OpenPGP context: our authentication code
|
|
|
|
|
rejects SHA-1 OpenPGP signatures, as recommended.]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Related Work]
|
|
|
|
|
:ident "related"
|
|
|
|
|
|
|
|
|
|
(p [A lot of work has gone into securing the software supply chain, often in
|
|
|
|
|
the context of binary distros, sometimes in a more general context; more
|
|
|
|
|
recent work also looks into Git authentication and related issues.
|
|
|
|
|
This section attempts to summarize how Guix relates to similar work that
|
|
|
|
|
we are aware of in these two areas. More detailed discussions can be
|
|
|
|
|
found in the issue tracker,(footnote [https://issues.guix.gnu.org/22883]).])
|
|
|
|
|
|
|
|
|
|
(p [The Update Framework,(footnote
|
|
|
|
|
[https://theupdateframework.io/]) (TUF) is a reference for secure update
|
|
|
|
|
systems, with a well-structured spec,(footnote
|
|
|
|
|
[https://github.com/theupdateframework/specification/blob/master/tuf-spec.md#the-update-framework-specification])
|
|
|
|
|
and a number of implementations. TUF is a great source of inspiration
|
|
|
|
|
to think about this problem space. Many of its goals are shared by
|
|
|
|
|
Guix. Not all the attacks it aims to protect against (Section 1.5.2 of
|
|
|
|
|
the spec) are addressed by what’s presented in this post: ,(tt [indefinite
|
|
|
|
|
freeze attacks]), where updates never become available, are not addressed
|
|
|
|
|
,(emph [per se]) (though easily observable), and ,(emph [slow retrieval attacks]) aren’t
|
|
|
|
|
addressed either. The notion of ,(emph [role]) is also something currently
|
|
|
|
|
missing from the Guix authentication model, where any authorized
|
|
|
|
|
committer can touch any files, though the model and
|
|
|
|
|
,(tt [.guix-authorizations]) format leave room for such an extension.])
|
|
|
|
|
|
|
|
|
|
(p [However, both in its goals and system descriptions, TUF is
|
|
|
|
|
biased towards systems that distribute binaries as plain files with
|
|
|
|
|
associated meta-data. That creates a fundamental impedance mismatch.
|
|
|
|
|
As an example, attacks such as ,(emph [fast-forward attacks]) or ,(emph
|
|
|
|
|
[mix-and-match attacks]) don’t apply in the context of Guix; likewise,
|
|
|
|
|
the ,(emph [repository]) depicted in Section 3 of the spec has little in
|
|
|
|
|
common with a Git repository.])
|
|
|
|
|
|
|
|
|
|
(p [Developers of OPAM, the OCaml package manager, adapted TUF for
|
|
|
|
|
use with their Git-based package repository,(footnote
|
|
|
|
|
[http://opam.ocaml.org/blog/Signing-the-opam-repository/]), later
|
|
|
|
|
updated to write Conex,(footnote [https://github.com/hannesm/conex]), a
|
|
|
|
|
separate tool to authenticate OPAM repositories. OPAM is interesting
|
|
|
|
|
because like Guix it’s a source distro and its package
|
|
|
|
|
repository,(footnote [https://github.com/ocaml/opam-repository]) is a
|
|
|
|
|
Git repository containing “build recipe”. To date, it appears that
|
|
|
|
|
,(tt [opam update]) itself does not authenticate repositories though; it’s up
|
|
|
|
|
to users or developer to run Conex.])
|
|
|
|
|
|
|
|
|
|
(p [Another interesting approach is to focus on the impact of
|
|
|
|
|
malicious modifications to Git repository meta-data ,(ref :bib
|
|
|
|
|
"torresarias2016:omitting"). An attacker with access to the repository
|
|
|
|
|
can modify, for instance, branch references, to cause a rollback attack
|
|
|
|
|
or a “teleport” attack, causing users to pull an older commit or an
|
|
|
|
|
unrelated commit. As written above, ,(tt [guix pull]) would detect such
|
|
|
|
|
attacks. However, ,(tt [guix pull]) would fail to detect cases where
|
|
|
|
|
metadata modification does not yield a rollback or teleport, yet gives
|
|
|
|
|
users a different view than the intended one—for instance, a user is
|
|
|
|
|
directed to an authentic but different branch rather than the intended
|
|
|
|
|
one. The “secure push” operation and the associated ,(emph [reference
|
|
|
|
|
state log]) (RSL) the authors propose would be an improvement.]))
|
|
|
|
|
|
|
|
|
|
(chapter :title [Conclusion and outlook]
|
|
|
|
|
:ident "conclusion"
|
|
|
|
|
|
|
|
|
|
(p [Guix now has a mechanism that allows it to authenticate
|
|
|
|
|
updates. If you’ve run ,(tt [guix pull]) recently, perhaps you’ve noticed
|
|
|
|
|
additional output and a progress bar as new commits are being
|
|
|
|
|
authenticated. Apart from that, the switch has been completely
|
|
|
|
|
transparent. The authentication mechanism is built around the commit
|
|
|
|
|
graph of Git; in fact, it’s a mechanism to ,(emph [authenticate Git checkouts])
|
|
|
|
|
and in that sense it is not tied to Guix and its application domain. It
|
|
|
|
|
is available not only for the main ,(tt [guix]) channel, but also for
|
|
|
|
|
third-party channels.])
|
|
|
|
|
|
|
|
|
|
(p [To bootstrap trust, we added the notion of _channel
|
|
|
|
|
introductions_. These are now visible in the user interface, in
|
|
|
|
|
particular in the output of ,(tt [guix describe]) and in the configuration
|
|
|
|
|
file of ,(tt [guix pull]) and ,(tt [guix time-machine]). While channel
|
|
|
|
|
configuration remains a few lines of code that users typically paste,
|
|
|
|
|
this extra bit of configuration might be intimidating. It certainly
|
|
|
|
|
gives an incentive to provide a command-line interface to manage the
|
|
|
|
|
user’s list of channels: ,(tt [guix channel add]), etc.])
|
|
|
|
|
|
|
|
|
|
(p [The solution here is built around the assumption that Guix is
|
|
|
|
|
fundamentally a source-based distribution, and is thus completely
|
|
|
|
|
orthogonal to the public key infrastructure (PKI) Guix uses for the
|
|
|
|
|
signature of substitutes. Yet, the substitute PKI could probably
|
|
|
|
|
benefit from the fact that we now have a secure update mechanism for the
|
|
|
|
|
Guix source code: since ,(tt [guix pull]) can securely retrieve a new
|
|
|
|
|
substitute signing key, perhaps it could somehow handle substitute
|
|
|
|
|
signing key revocation and delegation automatically? Related to that,
|
|
|
|
|
channels could perhaps advertise a substitute URL and its signing key,
|
|
|
|
|
possibly allowing users to register those when they first pull from the
|
|
|
|
|
channel. All this requires more thought, but it looks like there are
|
|
|
|
|
new opportunities here.]))
|
|
|
|
|
|
|
|
|
|
;; in-toto: https://www.usenix.org/conference/usenixsecurity19/presentation/torres-arias
|
|
|
|
|
|
|
|
|
|
(!latex "\n\\newpage\n\\balance\n")
|
|
|
|
|
(references))
|
|
|
|
|
|
|
|
|
|
;;; Local Variables:
|
|
|
|
|
;;; coding: utf-8
|
|
|
|
|
;;; ispell-local-dictionary: "american"
|
|
|
|
|
;;; compile-command: "make supply-chain.pdf"
|
|
|
|
|
;;; comment-start: ";;"
|
|
|
|
|
;;; End:
|