All right. Let's get going. Our next speakers are Paul and Malte from Azure Systems and
they're here to talk about remote attestation and reproducible builds.
Yeah, thanks. And I will start with some motivation. First, the topic of the talk is reproducible
builds for confidential computing and why we need it. So first, the motivation. Yeah,
confidential computing. What is the situation with confidential computing? We have trust issues,
especially when we're running in the public cloud. So yeah, first of all, we trust no one.
Well, that's not entirely true. We need some hardware we would have trust. So we have some
will have to trust the hardware manufacturer. And in all the other components that we are using,
we have to establish trust before we can rely on them. And we're doing this using remote attestation.
So a quick overview of a remote attestation based on the Rats RC. So here we have our three entities,
the attestor, the verify and the relying party. And the goal of the remote attestation procedure
is that relying party can place trust in the attestor system. So how are we doing this? Inside
the attestor, there's a testing environment and the target environment. And the testing environment
is doing measurement of the target environment. And then handing out some evidence that is
verified by the verify. And the verify uses two kinds of resources to verify the evidence. First,
the endorsement, which usually provides guarantees about the authenticity and integrity of the
evidence. And then some reference values that are compared to the claims that are inside the
evidence. And yeah, there were verify does verification and produces an attestation result.
And that attestation result is consumed by the relying party. And using this attestation result,
the relying party can place trust in the attestor system. So the aspect about this remote
attestation procedure we want to talk about here are the reference values. So as I already said,
we use the reference values to compare or to check the claims inside the evidence. And yeah,
some of these reference values represent the code identity of what we are actually running
inside of our TE. And often these values are hashes over what we are executing. And as we all
may know, hashes are one way functions. So it is really difficult to go back from a hash to what
was actually hashed. So many questions arise from this. Where do these hash values come from?
Who produces them? So who's our reference value provider? What do these hashes stand for? And
how can we establish trust into them? And often the answer is we just can't. And in this talk,
we want to present a way how we can establish trust and meaning to those reference values.
So why might this be a difficult task? So the main scenario we are talking about here is about
CVMs. And these CVMs have quite large TCBs. So we need to cover all of our software component with
these reference values. And yeah, there are quite a lot of components from where bootloader
kernel user space. We all need that stuff. Yeah, can be quite a lot of lines of code,
not always only some lines of code like in rushroom. But the more interesting question
is who is part of our trusted computing base? So software vendors usually and usually also a lot.
And there are different ways that we are including people into our trust base. So maybe the more
simple one is that we can consume code from other people. So well, that's quite usual. It's also
okay. We can order the code before we include it. And ideally, our language ecosystems provide us
with some mechanism to pin the dependencies that we use by some hash or so. So that's okay.
The second mechanism is more problematic. We could consume binary artifacts. And going back from
a binary to source is expensive. And yeah, typically, this is when we install packages using a package
manager, or if we use prebuilt VM images. And even if those binaries are signed, if we rely on the
signature, we include the signer into our trust domain. And then there's the third case that is
even worse. These are the situations where we cannot choose what we want to what is actually
running inside of our TCP. So this is, for example, the case when we have like something
hardware compatibility layer running below our guest OS in the CVM. Or if we are not able to
run customer provided firmware in the public cloud.
Okay, so talking a bit about the consequences here. Yeah, every software vendor we include on
our trust boundary could potentially run an attack on us. For example, by delivering malicious
reference values, so reference values for malicious binary. It's just really difficult to
check for us what these values stand for. And in the end, we have no insights what is actually
running in our system. So a simple solution could be we build everything from source, right?
Source is good. So we can audit the source. But usually we are not the consumers of the things
we built. So we're not the end users. And as a consequence, there's one remaining software
vendor in the trust boundary. And that are we. So that's not good too. And the actual goal here is
to provide a testable systems for the end user. And reproducible bugs can help us to do this.
And much of it will continue to tell you about reproducible bugs.
So thank you. So let's quickly talk about what reproducible builds actually are. So the basic
idea is you have software development principles where third parties or anyone can take the same
inputs and produce the same binary output. And this part of being independently verifiable
is really important to us. And let's just take a small step back and look at our perspective.
We are building a lot of software that is supposed to run inside of enclaves. For example, we're
building a full Kubernetes distribution with OS images and containers. And we really want people
not to have to just trust us because we are like reputable. We want people to take the stuff we
build, look at the source code, verify it, rebuild the binaries. And only then, only if they can
rebuild the same binaries, they can also just get to the same measurements and then they know that
they can trust us. So in the perfect world, this is what we would like to have. We just take the
source code, we put it into a function and we get out the reference values. But as you will see,
this is sadly not the reality today. So looking at this more closely, you have the source code
and then you have some kind of build process. And then what you get out is binary artifacts like the
firmware, the kernel, anything that goes into the user space applications. And from these, you derive
the hashes or other reference values used for remote attestation. And in reality, this is already
where you start running into problems because the software itself is not open and you cannot rebuild
it if the source code is not public, basically. So sometimes this is where you just have to stop.
But then if you're lucky, the source code is actually available. But that's when you run into a
whole different set of problems because if you want to build the same firmware and the same kernel
and the same user space and everything else, you notice that if you build your software,
it doesn't actually just depend on the source code. It actually also depends on timestamps and
randomness and inputs that you didn't know you had. And also it depends on tools and specific
versions of them. So let's say you actually managed to get all of this under control.
Then you can still run into this situation where you get the same firmware and everything else,
the whole stack, the whole TCB is the same. And you boot it in a trusted execution environment.
And still the evidence that you extract is different. And this is often the case if you
include anything in the measurement that is not part of the code, but it's actually dynamic,
like a timestamp at boot or the instance ID of your virtual machine. And yeah, in this case,
you have to do some run a policy engine on the other side, basically. Yeah, so this can be solved,
but it's also really annoying. Next, we will quickly look at who's already doing good work
in that field. So first is the AWS UEFI firmware. And this is used today to run
AMD, SCV, SNP virtual machines. And it's really nice. It's just EDK2,
OVMF firmware with some patches, but they also provide the full build system. So you can just
download it and rebuild it from source and actually get to the same measurements. Yeah,
another example is Constellation. This is our stuff that we built in there. We actually provide
every container image, any tool, the whole operating system, anything can be rebuilt from
source. And it's all reproducible. And yeah, then there's also the confidential containers,
cloud API adapter, and there's the peer ports images. They now have an option to build
images with MKOSI that are now also mostly reproducible. And we also have a GitHub repository
where we basically just wrote on all of the steps that are needed to take a general purpose
Linux distro and get also reproducible builds for that. And it's also documented and we show you
all of the steps that we took. So you can play around with that, which I think is like a good
starting point. So that's the repository if you want to have a look. Yeah.
So now some concrete help if you actually want to do this. So this is for building OS images
in particular. First of all, you need to pin your build tools. Basically, if you don't do that,
tomorrow you will have a newer version of a tool and you will have a different result.
And what we noticed is if we use something like NICS, we can pin all of the build dependencies
in a very nice way. And also we were able to patch a lot of the tools in NICS. So they actually
become reproducible. For example, we had a tool like MKFS for FAT partitions that was not
reproducible. We could make sure that the version NICS was actually
creating reproducible outputs. The second thing is about any things that you depend on. So that's
libraries of your building software or binary packages if you have to include them in your image.
First of all, you want to pin them. So you want to make sure that you know in advance the hashes
of everything that will be a dependency. And then it's not done just during that. You also have to
make sure that they are available in the future. So you have to archive them. You have to make them
available. And you also need to have a mechanism then to actually operate your log files because
if you just pin them, you will have a lot of security vulnerabilities in the future.
Yeah. And then it goes on. You really want to build every piece of software in a sandbox because
otherwise you don't actually know if your build is reproducible because it could depend on something
that is not actually there in the future. So user build system that does this, there's MKOSI for
building OS images. There's NICS and NICS OS, which are really great. And there's Bazel that also
uses Sandbox. Yeah. It will eliminate a whole class of issues. And then you also really want to restrict
build actions or install actions or any other kind of logic to only perform deterministic steps.
For example, I think the Cocoa project was using HashiCorp Packer and that has the issue that it can
run arbitrary steps, which means it could, for example, run up to get install. And then you
basically have no idea what version of something will be installed. And the same applies to Docker
files. So just use something that only does what you want. So this was our talk. There's some
important things we want to give to you to think about, learn about reproducible builds.
We want you to provide an open software stack for CC. And we want to enable the community to
reproduce the reference values that we put out into the world so we can remove ourselves from the
trust. So thank you. Thanks a lot. So I have a bit of a philosophical question. And that's related to
sort of the sort of like the relationship between reproducible builds and build provenance. So we
also had, like for the last time, there was last talk, there was a question about like S-bombs.
And this is of course something that is of increased importance because of the focus we have on supply
change security in general. And there is also people working on build provenance, right, where you
have the build hermiticity and you have like a record of how software was built. And that also
gives you like some guarantees of how you end up with a certain set of reference values,
even if it's not fully reproducible, right? So, but you know, from the provenance,
like what goes into this recipe. So do you have like any thoughts on like pros and cons or like
reflections around these two related topics? Yes, definitely. So I think first of all, if you're
able to have reproducible builds, you already basically have an S-bomb because it must be the
source code and anything that's locked in there. So it's actually like already the S-bomb there.
And then also, if you have an S-bomb, how do you trust the S-bomb? Because if someone just gives you
the S-bomb and it's not signed, it could be fake. And if someone does create like a trustworthy S-bomb,
it probably needs to be created in a confidential VM or something like that. So then you actually
make the whole problem a lot more complicated. So if you can just use reproducible builds and then
the problem is just fixed.
So the question was about if this also solves the problem of pinning the toolchain.
Yeah.
Yes. So the question is about like if you can trust the toolchain that bootstraps the whole system.
And yeah, I think you can bootstrap yourself from nothing. And I think also the NICS project
has some kind of bootstrapping where they do exactly that.
So that's it.