문제

I am building a Node.JS application that involves redirecting user input into a server-side command. Of course, that could be catastrophic to security, so I desire to run the child command inside an SELinux sandbox. (I do not want to run the entire application inside of a sandbox because I want the end users to each have their own workspace on the server.)

For example, consider the command cowsay. In order to run a sandboxed cowsay, you need simply sandbox cowsay. Other than the behind-the-scenes security differences, the interface of sandbox cowsay should be the same as that of plain cowsay.

However, Node.JS responds differently to these two approaches. Consider the code:

var spawn = require('child_process').spawn;                      
var cmd = spawn("cowsay", ["hello"]); // line A (no sandbox) 
var cmd = spawn("sandbox", ["cowsay", "hello"]); // line B (with sandbox)
cmd.stdout.on("data", function(data){
    console.log("stdout: "+data);
});
cmd.stderr.on("data", function(data){
    console.log("stderr: "+data);
});
cmd.on("exit", function(err){
    console.log("exit: "+err);
});

Here is the output of the version with line A:

$ node run.js
stdout:  _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

exit: 0

but here is the output of the version with line B instead:

$ node run.js
exit: 0

In other words, Node.JS does not seem to read the streams from sandbox's child process.

This test can be performed using any command. For example, suppose we used the python command interpreter. What happens here is that the non-sandboxed python waits for something to be fed into its stdin from Node.JS, but the sandboxed python simply exists with code 0, without waiting.

This behavior is exhibited only when SELinux is in "enforcing" mode. The sandbox version works fine when SELinux is in "permissive" (non-enforcing) mode.

What needs to happen in order for Node.JS to treat a sandboxed command the same as a non-sandboxed command?

도움이 되었습니까?

해결책

After playing around with this for a couple more weeks, we found a way to make the sandbox work as expected.

The issue seems to involve SELinux stopping libuv (the underpinnings of Node) from reading and writing a UNIX stream socket, which of course is required for Node's non-blocking I/O. One reason this took so long to find was that this error was being hidden from SELinux's audit by dontaudit rules.

We started by doing the following:

# semodule -DB  ## turns off dontaudit rules
# tail -f /var/log/audit/audit.log

Then in a different terminal, run the application. A bunch of SELinux audits appear, but most significantly, one that says:

type=AVC msg=audit(1370065734.791:5038): avc:  denied  { read write } for  pid=8887 comm="my_command" path="socket:[1258916]" dev=sockfs ino=1258916 scontext=unconfined_u:unconfined_r:sandbox_t:s0:c98,c807 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket

To make this into a policy, we did the following:

# grep "type=AVC msg=audit(1370065734.791:5038)" /var/log/audit/audit.log  | audit2allow -M unixsocketsandbox
# semodule -i unixsocketsandbox.pp

where unixsocketsandbox is a unique name for the policy. The policy (via cat unixsocketsandbox.te) reads as follows:

allow sandbox_t unconfined_t:unix_stream_socket { read write };

After this, the application works as expected, within the limitations of a sandbox.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top