godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Implement a Sandbox mode #5010

Open reduz opened 2 years ago

reduz commented 2 years ago

Describe the project you are working on

Godot

Describe the problem or limitation you are having in your project

It seems many users want to use Godot in a way where more complex content (such as scenes or Godot resources or even scripts) are downloaded from the internet. Unlike just sending simple variants, this is very insecure because the attack surface is huge.

Just limiting the Godot API (or script) to not accessing files and similar things is far from ideal because the more code you can run the more you can workaround to exploit the engine, and Godot is not designed for security (except in some areas like networking and protocols), it is designed for performance.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

A real sandbox mode to run projects should be provided to users, to make sure security is as high as possible and attack surfaces are as limited as possible.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

The way this would work takes advantage of the Godot architecture. Basically, it would split Godot into two processes, similar to software like Chrome works, one that talks to the low-level OS and another that runs the game in a sandbox (and sends commands to the first)

As such, this means that some of the core engine classes would effectively work as "proxies", like:

Meaning we would have a platform "sandbox" that has sandbox versions of the above classes, like DisplayServerSandbox that call the actual ones running outside of the sandbox (which is a regular Godot instance that starts as sandbox host and receives the sandbox API commands).

And not a lot more, so this means only those few classes are the ones that need to be "fortified" to avoid exploits, the rest can run entirely sandboxed.

For the sandbox itself, there are several available FOSS libraries that provide sandboxing, and alternatively WebAssembly can also be used to run the sandbox.

The best advantage of this approach is that it should not be very difficult to implement and provides a very easy and trustworthy way of running user content downloaded from the internet without danger.

A relatively straightforward way to implement this sandboxing could be via Wasmer, which is multi platform and portable.

If this enhancement will not be used often, can it be worked around with a few lines of script?

N/A

Is there a reason why this should be core and not an add-on in the asset library?

N/A

AlexDarigan commented 2 years ago

This would presumably allow for external resource files to be used fine without issues of arbitrary code execution?

theraot commented 2 years ago

There were previous proposals around resource packs:

Does this supersede them? Could this work together with those?

reduz commented 2 years ago

@AlexDarigan yes

@theraot I saw those, but sandboxing can't really be done at that level, it needs to be an entire engine instance being sandboxed.

winston-yallow commented 2 years ago

Is the goal of this to also make it more secure to run games by people you don't trust? Like for example when someone sends you a demo of their game to run. If yes, then we can not trust the executable as that could simply not have the sandbox mode enabled (or have whatever they want compiled in). So to run an untrusted game, we would:

  1. start the main godot process from a trusted executable
  2. then load the untrusted game as a sandboxed process

How would this process look for the users? In the simplest case I imagine just taking the pck and loading it with a trusted and sandboxed engine executable would work, but that would fail if the game needs any custom engine modifications.

So could we in theory have a kind of "trusted launcher" that can run the untrusted game executable (not pck), providing it with the necessary APIs for the proxies?

If this split is possible, then this may also provide new approaches for signed exectuables. The launcher could be signed as it won't change if someone compiles stuff into their version of the engine, and security would be ensured since it's sandboxed.

reduz commented 2 years ago

@winston-yallow well. if they send you the executable nothing much changes. If they send you the Godot project, maybe but it would need to work with the editor too (which I don't see much reason not to).

me2beats commented 2 years ago

See also https://github.com/godotengine/godot-proposals/issues/4495

wyattbiker commented 2 years ago

Is this something that docker can handle?

Calinou commented 2 years ago

Is this something that docker can handle?

This proposal is intended to be applicable to both clients and servers, so no. (Also, Docker isn't considered to be a sandboxing solution by its developers.)

derammo commented 2 years ago

I would design the sandboxing mechanism to be a documented "hard" API so that the sandbox side can have multiple implementations. I was planning to build such a bridge at some point in the future to run Lua scripts securely against Godot games to allow for modders to have access to white-listed nodes in the scene graph with firewalling on a property/function level.

This proposal has the potential to handle other languages as well. There is zero reason why code that runs in a sandbox that can only communicate via messages needs to be in GDScript (rather than JavaScript or Lua, etc.) You could of course decide to just remote GD objects and then do whatever language integration in the sandbox side the "normal" Godot way. But that means more and more Godot code runs on the sandbox side, which means Godot becomes responsible for maintaining the security of that. I would do a hard firewall line in the "remoting" protocol and let people implement the Lua sandbox in C++ (or C# because that is actually amazingly pleasant to work with Lua in) and the JavaScript sandbox in Node.js, etc. Then changes to Godot cannot affect the security of those sandboxes because no Godot code is used.

[edit: I might have mistaken this for a proposal to address sandboxing like you would need to run player-provided or modder code provided by someone else. This seems to just guard the core against bad plugins or scripts, right?]

wyattbiker commented 2 years ago

Is this something that docker can handle?

This proposal is intended to be applicable to both clients and servers, so no. (Also, Docker isn't considered to be a sandboxing solution by its developers.)

This is an article that is worth reading how to create the sandbox using docker and gVisor.

https://dev.to/narasimha1997/building-a-secure-sandboxed-environment-for-executing-untrusted-code-7e8

gituser50 commented 2 years ago

Will the sandbox be customizable e.g. that its possible to write your own custom logic in the host process to validate possibly dangerous calls (instead of blocking everything)?

Example:

// In the host process
file.open = func validator(filepath):
    // Ask user whether to allow file open.
    var allow = show_yes_or_no_dialog("Sandboxed process trying to open ", filepath)
    if allow:
          file.open(filepath, ...) // Calls the actual file open as this is the host process

Custom functions in the host process (suggestion)

Having possibility to add new custom functions to the host process (that can call multiple normally unsafe functions). This custom function would be called from sandboxed process.

func load_player_resources(url): // In host process
    // Validate url because its coming from sandboxed process and possibly unsafe
    // Download file and verify that its zip
    // Unpack files to "player_resources_cache" folder so they dont need to be downloaded again
    // Read files and return player new mesh or image texture whatever

This way only url needs to be validated (and not all of the function calls (Download file, Unpack files, Read file)). Contrarily if all those functions would be called seperately from sandboxed process, validating them would be pain and had to be done seperately for each function)

In case of game with mods and 3rd part content, basically sandbox could prevent everything by default (file access, network access, etc) and mods would just call those custom (big) functions that are in the main process (like that load_player_resources).

@reduz

Should networked functions be sandboxed as well?

Some reasons I can imagine:

ashtonmeuser commented 1 year ago

A simple POC of the WebAssembly approach @reduz mentioned was incredibly easy (even for a C++ novice like myself).

Check out the Godot Wasm addon. It's a bare-bones GDNative addon able to load Wasm module binaries and call their exported functions from GDScript. Seems like a reasonable way to implement user mod support as there is a hard boundary between Wasm guest modules and the host machine. There are some caveats (noted in the project) but none that seems like show stoppers.

I'd be more than happy to release to the Godot Asset Library if somebody can give my documentation a quick glance and compile the addon for Windows. This was spun up in a morning so I'm very open to PRs and issues.

fire commented 1 year ago

@ashtonmeuser The constraint is we're not currently allowing any rust into godot engine core, but gdnative and gdextension are good ways.

ashtonmeuser commented 1 year ago

The constraint is we're not currently allowing any rust into godot engine core, but gdnative and gdextension are good ways.

Seems wise. In my opinion, this is an ideal use case for an addon/extension and not something I'd petition to have merged into Godot core.

poVoq commented 1 year ago

There is also: https://github.com/WeaselGames/godot_luaAPI

leandrodreamer commented 1 year ago

idk if is it possible to disable those classes in runtime, but something like this i think would be cool:

print(Engine.is_sandbox_mode())
# false (mods can access those classes at this point)

Engine.set_sandbox_mode(true)
if Engine.is_sandbox_mode():
    load_mods()

Engine.set_sandbox_mode(false)
print(Engine.is_sandbox_mode())
# true (the flag can be set to true but never back to false)

good thing abt this is to allow the host code to do things with those classes before locking it and its pretty simple to set up, but it would require some more care from the dev, maybe the "set_sandbox_mode" could take some params to only allow specific directories so you can still do savegames and stuff like that

ashtonmeuser commented 1 year ago

Hey, folks! Don't mean to spam but just wanted to mention that some more work has gone into Godot Wasm and it now seems like a solid option for sandboxed executables e.g. mods. Many of Wasm's features are supported including import & export functions, export globals, and memory operations using the familiar StreamPeer interface. The project also supports Godot 4 and use as a Godot module. Plus, it's blazing fast and approaches GDNative performance (in compute-intensive benchmarks). Hope this helps somebody!

fire commented 1 year ago

We are reviewing and evaluating your work now. Commentary as soon I can get it.

Edited:

Posted a bug report about the lack of godot engine module support.

fire commented 1 month ago

We've experimenting with godot-sandbox which is a riscv emulator built on gdextension built for like all of godot's "10" platforms.

https://godotengine.org/asset-library/asset/3192

It's still early and but feel free to join the discord.