The Update Framework (TUF) is a flexible, open source framework and specification that developers can adopt into any software update system. It graduated as a CNCF project late last year and has become fairly influential in the world of cloud native-related security technology. But a lot of people are still unclear about what exactly it does or how. In this post, I’m hoping to offer a high-level explanation of what TUF is designed to do, and also talk a bit about how it has been developing as a result of its growing influence.
I co-maintain the TUF reference implementation and help run the specification editing process. I came to TUF from the Python community, where I showed interest in helping secure the Python Package Index (PyPI) software repository. Particularly, I was contributing to a long-term project that aims to integrate TUF into PyPI, which led me to get involved with TUF itself (I’ll write more about the PyPI integration in my next post).
An atypical project
TUF is an unusual open source project in that its canonical representation is actually its specification. This document describes how to lay out metadata describing a software update repository and then interact with that metadata from a client in order to achieve secure software updates.
Then, in association with the specification, the project has a number of implementations – many of which are open source and one of which is the reference implementation. The reference implementation has a symbiotic relationship with the specification so that major changes to the specification don’t happen until there’s an implementation of that change in the reference implementation. This assures that anything implementers are standardizing is tractable and sensible from their perspective. And it avoids a common issue with open source projects where the specification work is done in isolation of the notion of implementation.
Protecting software updates
TUF’s goal is simple: to protect software update systems. These systems are well known points of vulnerability and a common target for bad actors. Attacks aim to do things like prevent legitimate software updates, perform some kind of denial of service attack on your machine or force you to install undesirable content.
But ensuring effective security for software update systems is challenging. In the past, systems have typically secured their updates by signing packages. But that alone isn’t enough. If, for example, you sign a package, publish it, and then find out that the package contains a vulnerability (which is a fairly common occurrence in software updates), how do you indicate to the user that the package is no longer valid? And should the mere fact that a package was authenticated at some point in the past be enough to view it as secure now? That lacks what TUF calls “freshness.”
It’s also hard to manage encryption keys if you use them for package signing – how do you indicate to a software consumer that a key that was used to sign that package is no longer valid?
Even the act of signing packages themselves has an impact on the security of your implementation. When you sign a software package, you alter it. To account for that, most signing systems can generate what they call a “detached signature,” which is a separate file that says, “I’ve signed for that file.” But then, how do you protect the integrity of your detached signatures?
A predesigned framework
For all of these reasons, it’s best not to try and design your own security system for software updates. Better instead to adopt a vetted and pre-designed system, like TUF.
TUF is focused on all three high level attributes hinted at above: integrity, consistency, and freshness.
It offers the integrity that you get from a typical signing system, but it adds a notion of “freshness” by telling you whether you have the latest available software, or at least whether there’s newer software available. It then adds consistency, so it won’t install one package from your software repository that was created today and another package that’s several weeks older. That’s because the older one could have incompatibilities that cause system instability, or a known security vulnerability that can then be taken advantage of.
TUF achieves all three attributes by outlining a framework to follow and a reference implementation, which is a library written in Python.
The appeal for new operating systems
It’s worth noting that TUF was designed under the assumption that integrating projects would already have a software update system that its community was looking to supplement with better security features – as in the case of PyPI.
But we’re also seeing TUF being adopted in new update systems. Amazon’s AWS Labs recently announced their Bottlerocker operating system, which uses TUF to secure their updates, as does Google with its Fuchsia R&D OS. TUF also provided the basis for package signing integrity of Docker container images.
This expanding influence isn’t that surprising since the TUF specification was peer reviewed in three different ways: it originated in an academic research project, in the open source sense of being proven through multiple implementations and through having some of those implementations go through security audits. But it’s also led to TUF implementations being written in languages other than Python (in which the reference implementation is written). The Bottlerocket OS implementation of the TUF framework (Tough) is written in Rust, for example, and the implementation that forms the foundation of Docker Content Trust (Notary) is written in Go. There’s even a related open source project called Uptane which deploys many of TUF’s core design principles to secure software updates in the automotive industry. This has been run through ISO standardization, is already in use in many new and soon-to-be-released vehicles and can be seen as an independent implementation of TUF.
Evolving TUF to meet demand
These adaptations of TUF beyond its original scope speak to the value and flexibility of both the specification and core framework. But they’ve also indicated a need to evolve the TUF specification to meet the requirements of new types of software update systems.
The TUF community has been very receptive to that need and when people have presented us with use cases that require changes in the framework, we’ve typically offered proposals that would enable that use case to work.
We’re helping with the new version of Notary, for example, which is the software implementation that powers Docker Content Trust and is often described as an opinionated implementation of TUF. TUF developers proposed some standardizing solutions that will help Notary scale more easily and work better in air-gapped environments.
My primary focus right now, however, remains with Python. This involves adding a set of enhancements to the TUF reference implementation to support the PyPI model.
The proposal to integrate TUF into PyPI was first made a number of years ago but stalled because no one was available to do the work. Thanks to a grant, however, that work is underway at PyPI and the TUF community is working in parallel to ensure that the Python reference implementation is suitable for integration into PyPI.
If TUF has been quietly influential for a while now with an impact extending well beyond Python. Once it is integrated into PyPI, anyone installing Python software with a Pip install will be using TUF. No surprise, then, that we’re devoting most of our immediate attention to getting this work done.
Next time, I’ll share a few specific issues that we’ve been addressing in order to support the PyPI integration and also highlight some of our goals for improving TUF’s longer term sustainability and viability.