Hi, everyone. Yeah, my name is Miguel Sena, so I work for Microsoft and I'm mostly the
main of Landlack, which is a new Schedule Linux feature. And yeah, it's about sunbathing.
So this talk is about Rose Library. We wrote for Landlack and, well, we kind of had some
changes about compatibility. So yeah, just quick introduction and context to understand
the programmatic here. So yeah, why care about security? So here, well, it might be
abuse for some, but like every application can be compromised. Every application can be
trusted at first and during the lifetime of a process, it can, well, become malicious.
So yeah, as developers, there's, well, multiple problems. So we don't want to participate to
malicious actions performed by attackers through our software. And we kind of have a responsibility
for users, especially to protect their personal data. And yeah, there's also the, well, there
might be some issues about third-party code. So security is unboxing is a security approach
to isolate software and mainly to isolate them by dropping ambient access rights. So
in a shell, well, when you launch an application in, like, common in Existro, this application
can access a lot of files, including some, which are kind of private, like.ssh, for
example. So some mixing should not be confused with namespaces and containers, which is a
way to create kind of a virtualized environment. And Seccom is also something which is really
interesting for security purposes, but it's not about access control. It's about protecting
the kernel. That was initially the, well, initial goal of Seccom. So Linux is really
dedicated from the ground to bring some working features to Linux. So to bring some security
features to the kernel. So it is an access control system available to every processes.
You don't need to be a root or whatever. And it is designed to be embedded in applications.
So to create built-in sandboxing. It's the way to create one or even multiple layers
of new securities. So it comes kind of after all system-wide access control, which are
already in place. And so it's available on most distros nowadays. And if it is not the
case, well, I grant you to open an issue in your favorite distro. So about sandboxing
here, what's the interesting point about sandboxing and built-in application security?
If, well, that we can create tailored security policies and embedded them in the application.
So there's interesting things about that. And that might help to make it security like
invisible, which is kind of the main purpose here. We want to not bother users, but secure
them anyway. So because these securities policy can be embedded in the application, well,
it can use the application semantic. It can also use the application configuration transparently.
So you don't need to add another configuration stuff. It's not another layer of execution.
It's embedded in the application. And of course, well, if the configuration depends on user
interaction, well, it can adapt to this change of behavior. And one really interesting point
is, well, as developer, you want to test what you do. And you want to kind of get guarantees
that whatever you're developing is still working. And being able to embed security policies
in your application, make it possible to test them the same way that you can test every
other features. So that's really interesting. You don't rely on, let's say, Selenix being
installed on your test machine and so on. And it adapts to the application over time.
So if you have, well, a CI, which is well configured, you can test it and make sure
that, well, you can a bit, a bit add new features, updates the security policy and make sure that
everything was as expected. So speaking about the library and the Rust library, so the idea
was to create something which is Rusty, so identity to Rust. And for this, well, we wanted
to leverage strong typing so to get some developing guarantees. And so to follow some common patterns.
So many here, the builder pattern. So it's still a work in progress. It's working. But
yeah, we're working on improving the API and make it easier and more, yeah, easy to use
for competitive reasons. So this talk about these kind of compatibility requirements.
And yeah, so I'll talk about that. Some example of early-period users listed here. But yeah,
it's still in kind of beta. So let's start with some code example. So just as a warning,
this kind of simplified code, it's working. But yeah, for the demo, well, it's on demo,
but for this example, the idea is to make it simple to, well, to make it easier to understand.
So you can see at the left, there's a C code. And at the right, the exact same semantic,
but in Rust. So I will mostly talk about the Rust code. But yeah, you can take a look
at the C code to kind of see the difference between them and how Rust can be useful there.
So as I said, it is based on the builder pattern. So you create a rule set object here with
a rule set new. And from there, you kind of call different methods to, well, build the
object here. In this case, a root set. So a root set will contain a set of rules. And
yeah, at first, you define what you want to enforce, what you want to restrict, what
you want to deny by default. So in this case, these are two actions. The action to execute
files and the action to write on files. So obviously, it's not enough. But in this case,
it's easy to understand for the simple use case. And then, once you define the rule set
and what the rule set can handle, well, you can create it. And the rule set creation translates
to, you can see at the left, there's a London trade rule set. And this function is in fact
a C school. So in the Rust part, when you call the create method, it creates a new rule
set, which is backed underneath by a new file descriptor, dedicated to Larnock. And that
is a wrap in the rule set object. Then, if you want to add rules to allow some directory
to be, for example, executable, which is the case here. So, well, you open the slash user
directory and you make it, well, executable. So, allow access, access execute. And then,
you can add other rule you want for all the exception that should be legitimate for the,
well, legitimate use case. And then, you restrict the current process. Well, in fact, the current
thread. And from this point, the current thread can only execute files which are in slash
user. And it cannot write anything at all, actually. So, that was an introduction, quick
introduction to the library. And the thing is, Larnock is not a full feature access control
yet because, well, it is complex. And, well, to reach this goal, well, we need to spend
much more years to increment, well, to add new features to the link scale. Yeah. And
the thing is, well, sometimes you might add new features that enable to restrict more.
And sometimes we might add some features to restrict less. So, let's see what this means.
So, the first version of Larnock, which was released with a 5.13 kernel, basically allowed
to read, write, and do a lot of common stuff to restrict a lot of common files and actions.
But there was, like you can see here, there's three categories. So, first one, always denied,
was for the first version of Larnock, the actions that were always denied whenever you
sandboxed a thread. So, that was for, well, complexity in the development, but also security
reasons. So, for example, you are not able to execute set-ready binaries because it will
be kind of a way to bypass the sandbox. And there was some restriction on Ptrace, so you're
not allowed to debug an application process which is outside the sandbox. Obviously, it
will be a way to get out of the sandbox. So, that's not what we want.
So, the second version of Larnock had its new way, a new access write, which was a way
to repound files. So, at first, it was denied to change the predatory of a file for security
reasons because Larnock is based on five keys identification, and that was kind of complex.
So, but the second version, we implemented that, and then it became configurable. So,
one item less in the always denied box. In the third version of Larnock, so, all these
versions are new kernel releases, and in the third version, we added a new way to restrict
a file propagation. So, propagation in Larnock is to change the size of a file, and this was
always allowed before because it wasn't endowed. It was a bit complex to implement this in
the kernel at this time, but now it is possible. So, you can see that we can move items from
the always denied box to the configurable and from the always allowed box to the configurable
list. So, application compatibility. There's two main things in compatibility. It is forward
compatibility in a way that when you update your kernel, you still can use the old kernel
features. So, that's kind of common. And the backward compatibility in this case is, well,
when you're using a kernel feature, well, you might need the specification of the kernel
that supports this feature. And if your application is running its launch on an old kernel, well,
that feature might be missing. And the thing is, when you're developing an application,
well, you don't know on which kernel your application will run because, well, it's a
user choice and a distro choice. What comes with landlock is the ability to get the landlock,
what we call the ABI version. So, it's really just a number. That increments are started
at one, and then increments for each new set of features, which is added to the kernel.
So, to give you an idea, it's really simple to get this ID version. It's with a landlock
with a specific flag. So, yeah, it's a T code, but it's really simple. So, what we want
to do at first, well, these four main properties. The first one is to be able to, well, to make
it easy to use for developers, of course. So, we want something which is generic, which
kind of follows the build-up pattern because, well, it's kind of common and easy to use.
We want developers to focus on what they want to restrict, not the internal, well, implementation
in the kernel. And we want them to gradually go from a coarse-grain access restriction
to a fine-grain one. So, we don't want them to need to implement a fine-grain at first.
It might be difficult, too difficult. So, yeah, in the same way that we can incrementally
add new set of features, we can also incrementally restrict more and more of the time. So, no
need to be super strict at first. And, yeah, it should be simpler to write, well, for the
common cases. Okay. At first, the first improvement was to create group access rights. So, let's
say you know which landlock version is supported by the running kernel. Let's say it's a second
version. Then you can create a new root set which will get all the access rights which
are supported by this basic kernel. So, you just call the end-of-access with XFS from
all and then ABI2. And then you can do kind of the same when you're adding a new rule.
And this time, well, you want to add an exception on the slash result to make it readable. So,
in this case, there's two main groups, the from read and the from write. So, in, for
example, the from read includes reading a file, but also reading a directory. So, listing
a directory. Okay. Second property that we would like to have is being able to enforce
a strict restriction. So, even if we don't know on which kernel the application will
run, on some cases, we might want to be sure that all features are enforced and restricted.
There's two use cases here. The first one is to test it. If you want to sandbox an application,
you want to make sure that even if you're using all the sandboxing features, well, your
application will work as expected. So, that's really important. And you don't want to run
your application in an old kernel and kind of be fooled by the fact that your application
is running because there's no, well, not all secret features are enabled. So, you want
to cut these kind of issues in your CI. And also for security software, well, you want
to have some security guarantees. So, you want to have a way to fold the whole sandboxing
with all secret features that we embedded in our application. The third property is to
be able to enforce the best for security with some minimal requirements. So, that's kind
of the opposite. And this use case is mainly for end users because end user, well, you
don't know which kernel they will use. And so, you want to be able to enforce an opportunistic
sandboxing. So, if they have a new kernel, well, they will be more protected. If they
have an old kernel, they might not be protected at all, but that's not your choice, that's
not their choice. And at the end, they want to run your application anywhere. So, another
requirement is to be able to disable the whole sandboxing if some features which are required
may not be met. And this approach should be easier to write than others because it is
the most common thing to do. And the last property is being able to run, well, to configure
at runtime the sandboxing, but to make it in a way that you're running most of the codes.
So, the idea is to be able to have kind of the same code running everywhere, almost,
even if they don't have a recent kernel. Why that? Because you want to kind of identify
early kind of some issues which might be linked to the sandboxing code and that's if you have,
let's say, two users using a recent kernel and four users using an old kernel, well,
you might want to test as much as possible with all your users, even if they don't have
a new kernel. So, the first approach we took was, so we'll go quickly here, there's three
approach. The first one was to change, well, to add a new method to the root set builder
pattern. So, it was a simple method to set and set the best approach. So, if it was false,
it was required to have this feature. So, in the example, an application that needed
to move files from one directory to another needed to have the access effects refer access
right to allow this access. And if it wasn't the case, well, the something should not be
enforced, otherwise, it will break the application. So, that is a requirement. And in this case,
that was a way to kind of change the state of the builder over time. So, this is kind
of flexible, easy to understand, but some kind of cases. And, yeah, it makes the code
not really clean. Another approach was kind of to do the same, but this time, with instead
of two shifts, enable or disable, there were three ways to change it. The best-ifort way,
the soft requirement and the hard requirement. So, a way to make it best-ifort, a way to
make it error-out if there's any unsupported feature, and a way to disable the sandbox
without error if some feature were not supported. So, that wasn't ideal, neither. And the last
approach, which is currently working for us, is kind of a new one. So, the idea is to make
it still configurable and to follow all these properties, but to make it, well, a bit simpler
and still flexible. So, here, in a shell, well, you can make a new rule set that will
error-out if there's any unsupported features, but at the same time, you can specify which
feature is required to enable the sandbox or not. So, that's kind of specific, but, yeah,
should be better. So, going forward, there's a lot going on in this first library. A lot
to improve. You help, you help, you get a presentation, and I encourage you to, well,
then make your application or others. And, well, there's some tips if you want to get
some motivation here. It's a rewards program. So, thank you for attention. There's some
interesting link here. This talk was kind of a dance, but I hope you enjoyed. Thank you.