java 在Tomcat中执行shell命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12533799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Execute shell command in Tomcat
提问by Peter Ilfrich
So I have the following problem: I have a web service running inside a Tomcat7 server on Linux. The web service however has to execute some commands (mostly file operations such as copy and mount). Copy I've replaced with java.nio, but I don't think that there is a replacement for mount
.
所以我遇到了以下问题:我在 Linux 上的 Tomcat7 服务器中运行了一个 Web 服务。但是,Web 服务必须执行一些命令(主要是文件操作,例如复制和挂载)。我已经用 java.nio 替换了,但我认为没有替代mount
.
So I'm trying to execute shell commands out of my Tomcat Java process. Unfortunately it doesn't execute my commands. I've implemented the execution of shell commands in Java before. So my code should be correct:
所以我试图从我的 Tomcat Java 进程中执行 shell 命令。不幸的是它不执行我的命令。我之前在Java中实现了shell命令的执行。所以我的代码应该是正确的:
Process pr = Runtime.getRuntime().exec("mount -o loop -t iso9660 <myimage> <mymountpoint>");
pr.waitFor();
<myimage>
and <mymountpoint>
are absolute paths, so no issues there either.
<myimage>
并且<mymountpoint>
是绝对路径,所以也没有问题。
- I've debugged my commands and they are working when executed on the console.
- I've tried sending other commands. Simple commands such as
id
andpwd
are working! - I've tried using
/bin/bash -c "<command>"
, which didn't work. - I've tried executing a shell script, which executes the command, which didn't work.
- I've tried escaping the spaces in my command, which didn't work.
- 我已经调试了我的命令,它们在控制台上执行时正在工作。
- 我试过发送其他命令。简单的命令,例如
id
和pwd
正在工作! - 我试过使用
/bin/bash -c "<command>"
,没有用。 - 我试过执行一个 shell 脚本,它执行命令,但没有用。
- 我试过在我的命令中转义空格,但没有用。
So I've digged even deeper and now I'm suspecting some Tomcat security policy (Sandbox?), which prevents me from executing the command. Since security is no issue for me (it's an internal system, completely isolated from the outside world), I've tried a hack, which became quite popular just recently:
因此,我进行了更深入的挖掘,现在我怀疑某些 Tomcat 安全策略(沙箱?)会阻止我执行命令。由于安全性对我来说不是问题(它是一个内部系统,与外部世界完全隔离),我尝试了一种 hack,它最近变得非常流行:
System.setSecurityManager(null);
This didn't work either. I'm using Java7 and Tomcat7 on RHEL6. Tomcat7 is just extracted! I don't have any files in /etc/.. or any other folder than /opt/tomcat/, where I've extracted the zip from the Tomcat home page. I've searched the /opt/tomcat/conf folder for security settings, but all I could find was the file catalina.policy, where it didn't seem like I could set some security level for shell commands.
这也不起作用。我在 RHEL6 上使用 Java7 和 Tomcat7。Tomcat7刚刚解压!我在 /etc/.. 或除 /opt/tomcat/ 之外的任何其他文件夹中没有任何文件,我已经从 Tomcat 主页中提取了 zip 文件。我已经在 /opt/tomcat/conf 文件夹中搜索了安全设置,但我能找到的只有 catalina.policy 文件,在那里我似乎无法为 shell 命令设置一些安全级别。
Any ideas?
有任何想法吗?
采纳答案by Ian Roberts
It's generally a bad idea to use the single-string form of Runtime.exec
. A better option is to use ProcessBuilder
, and split up the arguments yourself rather than relying on Java to split them for you (which it does very na?vely).
使用Runtime.exec
. 更好的选择是使用ProcessBuilder
, 并自己拆分参数,而不是依靠 Java 为您拆分它们(这样做非常天真)。
ProcessBuilder pb = new ProcessBuilder("/bin/mount", "-o", "loop", /*...*/);
pb.redirectErrorStream(true); // equivalent of 2>&1
Process p = pb.start();
You say you're on RHEL so do you have selinux active? Check your logs and see if this is what's blocking you (I think it's audit.log you're looking for, it's been a few years since I've used selinux). If this does turn out to be the problem then you should probably ask on superuser or serverfault rather than SO...
你说你在 RHEL 上,所以你有 selinux 活动吗?检查您的日志,看看这是否是阻止您的原因(我认为这是您正在寻找的 audit.log,自从我使用 selinux 已经有几年了)。如果这确实是问题所在,那么您可能应该询问超级用户或服务器故障而不是 SO...
回答by Woot4Moo
A few things:
一些东西:
System.setSecurityManager(null);
you have just killed the security of your application.
您刚刚破坏了应用程序的安全性。
Yes, Tomcat is running as root. If I execute id I'm root as well.
是的,Tomcat 以 root 身份运行。如果我执行 id 我也是 root 。
Fix this immediately!
立即解决这个问题!
Now on to the question. You shouldnt have Tomcat executing anything, you need to defer this to a separate process whether that be a shell script or another Java program. This should also remove what (I hope) was a dependency on root running Tomcat. It should be possible to perform this command as a non-privileged user that cannot log into the system normally. You would do this by configuring /etc/fstab
and supplying that same user the permissions to do this. From a pure security POV the process that mounts should not be owned by the tomcat user. Nor should the tomcat user ever be root. So to recap:
1) Stop running Tomcat as root
2) Create a separate process outside of the context of Tomcat to run this mount
3) Create a tomcat user, this user should not be able to log into the system nor should it be a privileged user (admin,super user, etc)
4) Create a process user, this user should be configured exactly as the tomcat user
5) Edit /etc/fstab
giving the process user the necessary permissions to mount correctly.
现在来回答这个问题。您不应该让 Tomcat 执行任何操作,您需要将其推迟到单独的进程中,无论是 shell 脚本还是其他 Java 程序。这也应该消除(我希望)对运行 Tomcat 的 root 的依赖。应该可以作为无法正常登录系统的非特权用户执行此命令。您可以通过配置/etc/fstab
和向同一用户提供执行此操作的权限来完成此操作。从纯粹的安全 POV 来看,挂载的进程不应归 tomcat 用户所有。tomcat 用户也不应该是 root。所以回顾一下:
1)停止以 root 身份运行 Tomcat
2)在 Tomcat 上下文之外创建一个单独的进程来运行这个挂载
3)创建一个tomcat用户,这个用户应该不能登录系统,也不能是特权用户(admin,super user等)
4)创建一个进程用户,这个用户应该和tomcat用户一样配置
5) 编辑/etc/fstab
赋予进程用户正确安装所需的权限。
回答by GreyBeardedGeek
I've recently had to do something like this from a Swing app.
我最近不得不从 Swing 应用程序做这样的事情。
You'll probably be able to pull it off with ProcessBuilder, as in Ian's answer, but I found that once things start to get complex, it's easier to write a shell script that does what you want, enabling you to pass as few parameters as possible. Then use ProcessBuilder to invoke the shell script.
您可能可以使用 ProcessBuilder 完成它,如 Ian 的回答,但我发现一旦事情开始变得复杂,编写一个执行您想要的操作的 shell 脚本会更容易,使您能够传递尽可能少的参数可能的。然后使用 ProcessBuilder 调用 shell 脚本。
If you're invoking anything that has more than really minimal output, you'll also have to read the output and error streams to keep the process from blocking when the output buffers fill, as it seems you are already doing.
如果您调用的任何东西都不仅仅是真正的最小输出,您还必须读取输出和错误流,以防止在输出缓冲区填满时进程阻塞,因为您似乎已经在这样做了。
回答by David Levesque
I'm not sure if that's the problem you are having, but I've seen issues when Runtime.exec() is used without reading the associated output buffers. You can find a detailed explanation and potential solutions here. Reading the output and error streams can also help you figure out what's going on at the OS level when you run the command.
我不确定这是否是您遇到的问题,但我看到在使用 Runtime.exec() 而不读取相关输出缓冲区时出现问题。您可以在此处找到详细说明和潜在解决方案。读取输出和错误流还可以帮助您了解运行命令时操作系统级别发生的情况。
回答by Nelson Rodriguez
I use sudo -S
before command and for the tomcat7 user: tomcat7 ALL=(ALL) NOPASSWD:ALL
我使用sudo -S
before 命令和 tomcat7 用户:tomcat7 ALL=(ALL) NOPASSWD:ALL