|
|
Log in / Subscribe / Register

Containers and address space separation

By Jake Edge
May 1, 2019

LSFMM

James Bottomley began his talk at the 2019 Linux Storage, Filesystem, and Memory-Management Summit (LSFMM) by noting that the main opposition to his ideas was not present at the summit, which was likely to mean the ideas got a much easier reception than they would have otherwise. In particular, Peter Zijlstra and Ingo Molnar expressed some strong reservations to the work that Bottomley's colleague Mike Rapoport posted recently; none of those three were in attendance at LSFMM. The idea is to use address spaces to reduce the attack surface available to virtual machines (VMs) and containers such that kernel bugs of various sorts have less reach on multi-tenant systems.

Bottomley has been working with Rapoport on the idea for the container use case, but there are others, from Google and Oracle, who are trying to solve the same problems for VMs. Address spaces are the oldest and most secure mechanism for keeping tenants separate from one another, he said. Separating processes into their own address spaces is what was used to support multi-user systems, so there is around 50 years of history there. Part of the reason to extend the idea for VMs and containers is that address spaces have proven to work well as a security measure.

[James Bottomley]

For a KVM system, the security of the guest OS depends on the fact that the hypercalls make up a pretty tiny footprint within the host kernel. That interface is all that the guest OS has access to in the host. Prior to Spectre and Meltdown, that boundary would be enough for security but now programs can observe what others are doing using the flaws. So KVM developers would like to find ways to ensure that the guest cannot access anything else in the host kernel (even speculatively) by unmapping its code and by not providing access to the address space of its data.

Moving on to containers, he said that one of the big problems in container security is the kernel footprint that containers use. In the simplest case, containers have access to the entire system-call interface of the kernel, which means they are exposed to a "broad swath of the kernel". If you listen to VM people, he said, they will claim that the access to all of the 360 or so system calls is what makes containers inherently far less secure than VMs. But if you measure what is actually being used by containers, "that is bollocks"; generic containers are something like two to five times less secure than hypervisors (in terms of the amount of kernel code traversed), not the hundreds of times that VM proponents like to suggest.

There is a "semantic gap" between the system calls that a guest VM can make and the code that actually gets executed in the host kernel. The security of VMs is partly based on the fact that it is actually difficult to create an exploit at the system-call level that will translate across the hypercalls into the host kernel. So there is lots of exposed code in the guest OS, but a great deal less in the host OS because of the hypervisor interface. Container developers would like to get back the properties that the semantic gap provides, without reverting to having the gap itself, since it is the lack of this gap that gives containers much of their power.

But in order to reduce the kernel footprint, projects like gVisor and Nabla Containers create sandboxes to handle some of the kernel work in user space. For gVisor, the kernel calls have been rewritten in Go, while Nabla Containers took the approach of reducing the projection of the containers into the kernel by restricting which system calls can be made and emulating the rest in user space.

The price for those approaches is a "massive semantic gap", he said. It has high security properties, but it causes problems for memory management and elsewhere. He is looking to thin out the sandbox; ideally via separate address spaces for each container or VM. If every kernel namespace could have objects that were private to that namespace, that would go a long way toward solving this problem, he said. It is primarily a protection for multi-tenant systems, so he is not suggesting that the feature would be turned on everywhere.

Trond Myklebust asked how these ideas differed from a microkernel architecture. In a microkernel, the filesystem driver and the block device driver would each be in their own address spaces, Bottomley said, so sharing filesystems between containers would be problematic because the two containers would be accessing the same filesystem address space. What he is looking for is to separate address spaces on a per-tenant basis, not per subsystem.

Another attendee noted that in a microkernel, a fault in the filesystem driver, for example, only affects that component, not the rest of the system. The architecture will make it difficult to separate a shared filesystem and all of its data structures; it is a question of which resources you are protecting.

Bottomley believes that two unrelated containers sharing a filesystem is a niche use case in the cloud. Since most don't do it, we can afford to make them pay a huge penalty when they do, he said. In terms of fault isolation, a microkernel will simply restart a failing driver, but what is needed in the multi-tenant case is to kill the tenant that caused the fault. The idea is to punish a malicious tenant. The difference is that a microkernel provides an address space per subsystem, while he is looking for an address space per tenant; the design goals and security properties are different in the two cases.

The question of performance was raised; can the performance that will be lost by doing this address-space separation ever be recovered? Bottomley believes that it is a hardware problem. He noted that virtualization was slow initially, so the hardware vendors stepped up to make it faster; the same could happen here. Beyond that, this feature is for security; anything is acceptable in the name of security, he (half) joked.

Matthew Wilcox said that he did not buy the semantic gap argument. He noted that in a previous life as a Java programmer he had a colleague who was able to accidentally corrupt the BIOS in his machine with a Java program. Bottomley agreed; the semantic gap argument is a form of security through obscurity, he said. There are two main approaches to providing security for containers: either guarding against malicious system calls, which sometimes works and sometimes doesn't, or emulating system calls in user space.

Returning to the shared-filesystem question, Ted Ts'o noted that often the base images for containers (such as RHEL) are shared. Bottomley said that typically those are shared in a read-only fashion, which is a solved problem. Read-only pages in the page cache can be shared, but sharing read-write pages is not a common cloud-container pattern.


Index entries for this article
KernelMemory management/Address-space isolation
ConferenceStorage, Filesystem, and Memory-Management Summit/2019


to post comments

Containers and address space separation

Posted May 1, 2019 23:39 UTC (Wed) by luto (subscriber, #39314) [Link] (2 responses)

I don’t expect hardware to accelerate these address space tricks. Instead, I expect hardware to help with control flow integrity. Intel’s upcoming CET will do this via “shadow stacks”, and I don’t see why one would want to use the types of proposed tricks on a CPU with CET.

Containers and address space separation

Posted May 2, 2019 6:29 UTC (Thu) by Lionel_Debroux (subscriber, #30014) [Link] (1 responses)

Was CET improved since nearly three years ago, when PaXTeam dissected what was announced and found it pretty limited ( https://grsecurity.net/effectiveness_of_intel_cet_against... ) ? Even if it was, it's going to be a while before most x86_64 cores in the real world have it - just like SMAP. Further dwarfing the impact of CET is the fact that there are more non-x86_64 cores than x86_64 cores in the wild, though the server market, which is increasingly running container-based workloads, keeps using mostly x86_64 cores, for various reasons...

Of course, PaX's RAP, despite being effectively open source in the PaX/grsecurity patch - both the latest official public one from April 2017 and the leaked tree from late 2018 - is definitely trademarked and also maybe successfully patented by now, so while outdated versions thereof can be independently benchmarked and examined (with some elbow grease), it's not a practical solution for nearly anyone...

Containers and address space separation

Posted May 2, 2019 14:49 UTC (Thu) by luto (subscriber, #39314) [Link]

The part of CET I'm talking about is the shadow stack part, which is really quite strong. And the "SCI" proposal, as far as I know, is even weaker than CET in controlling the targets of indirect branches and calls.

So yes, I stand by my assertion that, as far as I can tell, CET is strictly stronger than SCI.

Containers and address space separation

Posted May 2, 2019 8:43 UTC (Thu) by gebi (guest, #59940) [Link] (2 responses)

"Read-only pages in the page cache can be shared, but sharing read-write pages is not a common cloud-container pattern."

hm... i don't think that matches the real world.
It's normal in the kubernetes world to share filesystems between containers within a pod.

Containers and address space separation

Posted May 2, 2019 15:47 UTC (Thu) by matthias (subscriber, #94967) [Link] (1 responses)

I am not sure whether this is a problem. If I read the article correctly (and understand correctly what a pod is, as I do not have experience with kubernetes), this would not match the use case that unrelated containers share a filesystem, exactly because the containers are related. According to https://kubernetes.io/docs/concepts/workloads/pods/pod/, containers within a pod share the namespaces. So they would not be put into different address spaces anyway. If I understand correctly, the proposed patch would be used to separate different pods, which are not expected to share a writeable filesystem.

Containers and address space separation

Posted May 2, 2019 20:28 UTC (Thu) by bjhaid (subscriber, #115252) [Link]

> According to https://kubernetes.io/docs/concepts/workloads/pods/pod/, containers within a pod share the namespaces

By default they only share network, ipc and uts namespaces. k8s will usually do a bind mount to share files/folders across containers

Containers and address space separation

Posted May 2, 2019 16:39 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link]

This logic is just lovely: I don't care about feature X. Hence, hurting people who use X is ok (this becomes even better when substituting IBM for I).

Containers and address space separation

Posted May 2, 2019 18:11 UTC (Thu) by flussence (guest, #85566) [Link] (8 responses)

Regarding syscall surface area, I would really like it if vanilla Linux adopted the idea from the mobile/web/OpenBSD world of declaring needed permissions up front.

One of the frequent criticisms I see of seccomp-based security is that it can't come close to pledge's ease of use because it can't know which libc calls map to which syscalls. That information could easily be gathered at compile time though: if it were the norm for GCC to annotate all ELF files with a map of syscalls they're going to invoke, things like firejail and whatever the enterprise world uses this week would be much easier to set up. It'll probably only work when the call chain and syscall number can be statically determined at compile time, but I'd argue anything else is a code smell.

Containers and address space separation

Posted May 2, 2019 20:49 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link] (7 responses)

You'll very likely find code in glibc where the syscall numbers aren't known at compile time because the syscall being used depends on the capabilities of the running kernel.

OTOH, this "syscall surface area" is just a completely bizarre metric. Not all syscalls are equal to each other. The implementation of the pause system call is certainly a lot simpler than that of sendfile syscall. The same goes for the idea of working around unknown/ hypothetical kernel bugs by using sime kind of not exactly trivial middleware whose unknown/ hypothetical bugs don't matter because 'we' prefer not to think about them, as we otherwise couldn't "sell" our non-solution.

Containers and address space separation

Posted May 2, 2019 21:01 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link] (6 responses)

libc knows which syscalls will be used and can create a conservative list of all possibilities.

Dynamic syscalls through the syscall() function will have to be done manually.

Containers and address space separation

Posted May 2, 2019 21:09 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link] (5 responses)

Considering that dynamic linking exists, the code which will be executed at some specific time isn't even known at compile time. Even if it was, the compiler would need to determine all possible runtime code paths. This ought to solve the halting problem as a side effect :-).

Containers and address space separation

Posted May 2, 2019 21:15 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link] (4 responses)

That's fine. Just save your syscall lists in an ELF section. Then the sandbox can make the list of all calls from all linked libraries.

This won't cover dlopen() dependencies, but not much software that people would like to sandbox actually uses dlopen().

The idea here is not to create a 100% foolproof solution, but provide an easy to use mitigation mechanism for potential breaches. OpenBSD did it right with pledge().

Containers and address space separation

Posted May 2, 2019 22:20 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link] (3 responses)

The problem is that the C library is one of those linked libraries. Hence, this list would contain pretty much all system calls unless an exhaustive pre-execution analysis of the code which will be executed can be used to limit this set to the actually needed ones. I'm usually critical of OpenBSD but they're certainly competent programmers. Hence, they didn't even try to go down this way.

Such a mechanism, however implemented, still wouldn't be particularly useful, because the code implementing it can contain unknown, exploitable bugs as well. Directing energies towards finding and fixing real bugs makes more sense and then spending time on "miracle cures" whose usefulness is mainly "enables someone to sleep soundly despite ...".

Containers and address space separation

Posted May 2, 2019 22:25 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link] (1 responses)

libc has to be treated specially. Perhaps with annotations for each symbol, rather than the overall code. Per-symbol annotations won't really work for larger libraries as you'd have to analyze transitive dependencies between functions, but libc is fairly simple.

A full-scale pledge() would work better (hey, LSM folks, are you here?!?) but the only workable mechanism in Linux right now is BPF-based seccomp.

Containers and address space separation

Posted May 3, 2019 14:29 UTC (Fri) by rweikusat2 (subscriber, #117920) [Link]

But the C library isn't special. It's just another library. For additional complications, other code usually doesn't do direct syscalls at al but calls C library function which might or might not end up doing a or some syscalls. The idea might seem simple, but this is a non-starter.

Considering that it's also still completely useless save as psychological band-aid, there's no reason to worry about this. This whole "Security by making it more complicated!"-approach is completely misguided. There is no miracle cure here, despite there's no shortage of people trying to sell miracle cures. As someone put it on some web site a long time ago: "If you want security, you do code audits".

Containers and address space separation

Posted May 6, 2019 22:18 UTC (Mon) by nix (subscriber, #2304) [Link]

The problem is that the C library is one of those linked libraries.
Quite. Also, glibc calls dlopen() under the covers, incurring all the problems you noted that dlopen() causes for this scheme: when you do a name lookup, say, or a getent, or any other service lookup (including user dbs, etc), or when you do an iconv(). This means that, yes, a hello world program that does nothing but a printf() could perfectly well invoke dlopen(), even when statically linked. Sure, it can only dlopen() stuff that's mentioned in /etc/nsswitch.conf or in the iconv modules dirs, but isn't that rather too much special-casing for an allegedly-general security scheme?

Containers and address space separation

Posted May 3, 2019 8:57 UTC (Fri) by nilsmeyer (guest, #122604) [Link] (2 responses)

> Prior to Spectre and Meltdown, that boundary would be enough for security but now programs can observe what others are doing using the flaws.

I worry that there is going to be a lot of overhead created for a CPU bug that is likely to be fixed with the next CPU generations.

Containers and address space separation

Posted May 3, 2019 10:55 UTC (Fri) by excors (subscriber, #95769) [Link] (1 responses)

Meltdown is just a bug, but the Spectre class of issues is a pretty fundamental consequence of an optimisation technique that CPUs have relied on for decades. I don't think it's apparent that it can ever be fixed in hardware, without an unacceptably massive performance regression.

Containers and address space separation

Posted May 4, 2019 22:10 UTC (Sat) by davecb (subscriber, #1574) [Link]

Non-speculatively executing ready-to-run instructions in other threads will also provide a lovely performance boost, without allowing one to build a predictable side channel in your own thread. In part this has been done, and I suspect that others are happily copying the idea as we speak (;-))


Copyright © 2019, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds