Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve configuration of compatibility #3

Open
l0kod opened this issue Feb 16, 2025 · 4 comments
Open

Improve configuration of compatibility #3

l0kod opened this issue Feb 16, 2025 · 4 comments

Comments

@l0kod
Copy link
Member

l0kod commented Feb 16, 2025

For now, the configuration is close to the Rust library, but we need to make it simpler to use and easier to understand. See landlock-lsm/rust-landlock#58

@l0kod
Copy link
Member Author

l0kod commented Mar 3, 2025

Here is a new proposal replacing the current best_effort, soft_requirement, and hard_requirement compatLevel with tailored errorIfAnyUnsupported, cancelSandboxingIfAnyUnsupported, and (a new) errorIfAnyUnusablePath:

{
	"ruleset": [
		{
			"handledAccessFs": [ "v6.all" ]
		},
		{
			"handledAccessFs": [ "refer" ],
			"cancelSandboxingIfAnyUnsupported": true
		}
	],
	"pathBeneath": [
		{
			"allowedAccess": [ "execute", "read_file", "read_dir", "refer" ],
			"parentFd": [ ".", "/usr", "/etc", "/proc", "/tmp" ],
			"errorIfAnyUnusablePath": true
		},
		{
			"allowedAccess": [ "write_file", "remove_dir", "remove_file", "make_char", "make_dir", "make_reg", "make_sock", "make_fifo", "make_block", "make_sym", "refer", "truncate" ],
			"parentFd": [ "/tmp" ]
		}
	]
}

With this first configuration:

  • cancelSandboxingIfAnyUnsupported: No restriction at all would be enforced if the running kernel does not support refer. In this case, this should be used when the sandboxed app must be able to create hardlinks (which is only possible with the refer access right).
  • errorIfAnyUnusablePath: The sandboxing will failed with an error if one of the [ ".", "/usr", "/etc", "/proc", "/tmp" ] paths do not exist. This should be used when one of these paths should exist for the sandboxed app to correctly work.

	"ruleset": [
		{
			"handledAccessFs": [ "v6.all" ]
		}
	],
	"errorIfAnyUnsupported": true,
	"pathBeneath": [
		{
			"allowedAccess": [ "v6.read_execute" ],
			"parentFd": [ ".", "/usr", "/etc", "/proc", "/tmp" ]
		},
		{
			"allowedAccess": [ "v6.read_write" ],
			"parentFd": [ "/tmp" ]
		}
	]

With this second configuration:

  • errorIfAnyUnsupported: The sandboxing will failed with an error if any of the handled access right are not supported by the running kernel. This should be used to for test environment to make sure the full sandboxing is tested, or in tailored systems were we know the running kernel should support these features.

Any opinion @gnoack, @rata, @Foxboron, @rgacogne?

@Foxboron
Copy link

Foxboron commented Mar 3, 2025

I might be missing something, but a potential issue I see is that these are three flags where two are generic, but one is specific to one part of landlock. errorIfAnyUnusablePath only cares about file paths, but should we then also have specific restrictions for things like network and IPC?

Would this be more complicated then the current compat levels which are all generic?

@l0kod
Copy link
Member Author

l0kod commented Mar 4, 2025

I might be missing something, but a potential issue I see is that these are three flags where two are generic,

That's right, the two generic flags are errorIfAnyUnsupported and cancelSandboxingIfAnyUnsupported. Any better name?

but one is specific to one part of landlock. errorIfAnyUnusablePath only cares about file paths, but should we then also have specific restrictions for things like network and IPC?

In some cases we might want to know if a file is missing, hence the specific errorIfAnyUnusablePath flag. Thinking about that again, I guess we should remove this specific flag. If there is a real need for that, it will be easy to extend the schema. The important point is, by default, to not error out if a path doesn't exist, otherwise it would make configuration writing and maintenance very unpractical. Testing for existence of a required file should be done by the sandboxed app/container anyway.

Would this be more complicated then the current compat levels which are all generic?

This proposal indeed brings narrower configuration bits. The current compat levels looks too generic and might be confusing. Soft and hard requirements are not clear enough: opencontainers/runtime-spec#1241 (comment)
See this discussion about the two use cases landlock-lsm/rust-landlock#12 (comment) and the FOSDEM talk.

This new approach should be easier to understand and meet the requirements for tests and compatibility.

@rata
Copy link

rata commented Mar 4, 2025

This might be personal, but cancelSandboxingIfAnyUnsupported gave me the wrong impression by only looking at the name: I thought sandboxing was aborted (cancelled). What something like ignoreUnsupported? But this might be just me, feel free to ignore me if you like the current name :)

Also, If I'm getting the right meaning for all of this, it seems to me that all the flags are basically the same: return an error if <the appropiate thing for this context> fails. So, in my mind, this could be the same flag. I'm bad with names, but like: enforce_policy: <string>.? For example:

{
	"ruleset": [
		{
			"handledAccessFs": [ "v6.all" ]
		},
		{
			"handledAccessFs": [ "refer" ],
			"enforce_policy": "FailOnUnknownAccess"
		}
	],
	"pathBeneath": [
		{
			"allowedAccess": [ "execute", "read_file", "read_dir", "refer" ],
			"parentFd": [ ".", "/usr", "/etc", "/proc", "/tmp" ],
			"enforce_policy": "FailOnMissingDir"
		},
		{
			"allowedAccess": [ "write_file", "remove_dir", "remove_file", "make_char", "make_dir", "make_reg", "make_sock", "make_fifo", "make_block", "make_sym", "refer", "truncate" ],
			"parentFd": [ "/tmp" ]
		}
	]
}

And errorIfAnyUnsupported can just be a default enforce_policy, like:

	"ruleset": [
		{
			"handledAccessFs": [ "v6.all" ]
		}
	],
	"pathBeneath": 
           "enforce_policy": "FailOnUnsupported"
           "rules": [
		{
			"allowedAccess": [ "v6.read_execute" ],
			"parentFd": [ ".", "/usr", "/etc", "/proc", "/tmp" ]
		},
		{
			"allowedAccess": [ "v6.read_write" ],
			"parentFd": [ "/tmp" ]
		}
	]

This would be a default policy that each rule can override with a "more local" policy like in the previous example. But if you want the behavior of having unchanged rules between prod and testing and have a single thing to set to enforce them all (like the global errorIfAnyUnsupported), maybe pathBeneath can also have a "override_rules_policy: FailOnError"

And maybe the value foe the policy can always be FailOnError and that means any errors to apply that will create a failure. Maybe we don't need to be so specific in the values? I think I prefer FailOnError for all too, it seems simpler to me.

That wouldn't work if you want to differentiate errors of missing dirs vs missing paths in the pathBeneath section, but I think we can start with FailOnError and add FailOnMissingPath in the future, if needed.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants