I’m a recent but dedicated convert to Silverblue, which I run on my main home laptop and which I’ll be putting onto my work laptop when I’m due a hardware upgrade in a few months’ time. I wrote an article about Silverblue over at Enable Sysadmin, and over the weekend, I moved the laptop that one of my kids has over to it as well. You can learn more about Silverblue over at the main Silverblue site, but in terms of usability, look and feel, it’s basically a version of Fedora. There’s one key difference, however, which is that the operating system is mounted read-only, meaning that it’s immutable.
What does “immutable” mean? It means that it can’t be changed. To be more accurate, in a software context, it generally means that something can’t be changed during run-time.
Important digression – constant immutability
I realised as I wrote that final sentence that it might be a little misleading. Many programming languages have the concept of “constants”. A constant is a variable (or set, or data structure) which is constant – that is, not variable. You can assign a value to a constant, and generally expect it not to change. But – and this depends on the language you are using – it may be that the constant is not immutable. This seems to go against common sense[1], but that’s just the way that some languages are designed. The bottom line is this: if you have a variable that you intend to be immutable, check the syntax of the programming language you’re using and take any specific steps needed to maintain that immutability if required.
Operating System immutability
In Silverblue’s case, it’s the operating system that’s immutable. You install applications in containers (of which more later), using Flatpak, rather than onto the root filesystem. This means not only that the installation of applications is isolated from the core filesystem, but also that the ability for malicious applications to compromise your system is significantly reduced. It’s not impossible[2], but the risk is significantly lower.
How do you update your system, then? Well, what you do is create a new boot image which includes any updated packages that are needed, and when you’re ready, you boot into that. Silverblue provides simple tools to do this: it’s arguably less hassle than the standard way of upgrading your system. This approach also makes it very easy to maintain different versions of an operating system, or installations with different sets of packages. If you need to test an application in a particular environment, you boot into the image that reflects that environment, and do the testing. Another environment? Another image.
We’re more interested in the security properties that this offers us, however. Not only is it very difficult to compromise the core operating system as a standard user[3], but you are always operating in a known environment, and knowability is very much a desirable property for security, as you can test, monitor and perform forensic analysis from a known configuration. From a security point of view (let alone what other benefits it delivers), immutability is definitely an asset in an operating system.
Container immutability
This isn’t the place to describe containers (also known as “Linux containers” or, less frequently or accurately these days, “Docker containers) in detail, but they are basically collections of software that you create as images and then run workloads on a host server (sometimes known as a “pod”). One of the great things about containers is that they’re generally very fast to spin up (provision and execute) from an image, and another is that the format of that image – the packaging format – is well-defined, so it’s easy to create the images themselves.
From our point of view, however, what’s great about containers is that you can choose to use them immutably. In fact, that’s the way they’re generally used: using mutable containers is generally considered an anti-pattern. The standard (and “correct”) way to use containers is to bundle each application component and required dependencies into a well-defined (and hopefully small) container, and deploy that as required. The way that containers are designed doesn’t mean that you can’t change any of the software within the running container, but the way that they run discourages you from doing that, which is good, as you definitely shouldn’t. Remember: immutable software gives better knowability, and improves your resistance to run-time compromise. Instead, given how lightweight containers are, you should design your application in such a way that if you need to, you can just kill the container instance and replace it with an instance from an updated image.
This brings us to two of the reasons that you should never run containers with root privilege:
- there’s a temptation for legitimate users to use that privilege to update software in a running container, reducing knowability, and possibly introducing unexpected behaviour;
- there are many more opportunities for compromise if a malicious actor – human or automated – can change the underlying software in the container.
Double immutability with Silverblue
I mentioned above that Silverblue runs applications in containers. This means that you have two levels of security provided as default when you run applications on a Silverblue system:
- the operating system immutability;
- the container immutability.
As a security guy, I approve of defence in depth, and this is a classic example of that property. I also like the fact that I can control what I’m running – and what versions – with a great deal more ease than if I were on a standard operating system.
1 – though, to be fair, the phrases “programming language” and “common sense” are rarely used positively in the same sentence in my experience.
2 – we generally try to avoid the word “impossible” when describing attacks or vulnerabilities in security.
3 – as with many security issues, once you have sudo or root access, the situation is significantly degraded.
Great writeup. I want to expand on implications of immutability in
programming languages. Your point about the mutability of so-called
“constants” in many langs is a super-important one. But it only scratches the surface.
An immutable language has many nice properties. For example
referential transparency, which gives rise to equational reasoning,
which is a very useful tool when writing and refactoring programs.
And if you care about correctness and security.
Immutability opens other interesting possibilities, such as
call-by-need (a.k.a. “laziness”). Ever wanted to explore the tree
of all possible chess games? It is feasible, even though you cannot
fit it all in memory 🙂
Of course there is a trade-off. In the general case you need GC,
which rules it out for some real-time or resource constrained
applications.
LikeLike
(GC = garbage collection)
LikeLike
I miss working with you on PKI Fraser! A fantastic addendum to a great article!
LikeLike
I miss working with you in the PKI world Fraser… This is a fantastic addendum to a great article!
LikeLike
I super love this topic. It’s an interesting conversation with your traditional sysadmins who struggle to wrap their existing notions of system maintenance around this paradigm.
LikeLike