subprocess.Popen设置shell=False执行含iptables命令失败,如何解决?
问题原因
- 当
shell=False时,subprocess.Popen会将传入的参数列表的第一个元素作为可执行程序,列表后续所有内容都作为该程序的传入参数,不会自动处理;分隔的多条命令逻辑。你拆分后的列表第一个元素是addgroup,后续的iptables、ip6tables、sg相关内容全部被当成了addgroup的参数,自然会报Unknown option类的错误。 - 你直接对包含
;的多命令串做shlex.split本身就不符合预期:;是shell的命令分隔符,不属于单条命令的参数部分,拆分后会出现no-external-internet;、ACCEPT;这类末尾带分号的错误参数。
解决方案
有两种常用的修复方案,可根据你的场景选择:
方案1:拆分命令分别执行(推荐,安全性最高)
将5条独立的命令拆分,依次调用Popen执行,执行前要确认上一条命令执行成功,避免后续依赖命令出错:
import os import subprocess import shlex HOST = "localhost" PORT = 8000 group_name = "no-external-internet" cwd = "some directory" # 1. 执行addgroup cmd = f"addgroup --system {group_name}" p = subprocess.Popen(shlex.split(cmd), preexec_fn=os.setsid) p.wait() if p.returncode != 0: raise RuntimeError("addgroup执行失败") # 2. 执行第一条iptables规则 cmd = f"iptables -A OUTPUT -m owner --gid-owner {group_name} -d {HOST} -p tcp --sport {PORT} -j ACCEPT" p = subprocess.Popen(shlex.split(cmd), preexec_fn=os.setsid) p.wait() if p.returncode != 0: raise RuntimeError("iptables ACCEPT规则配置失败") # 3. 执行第二条iptables规则 cmd = f"iptables -A OUTPUT -m owner --gid-owner {group_name} -j REJECT" p = subprocess.Popen(shlex.split(cmd), preexec_fn=os.setsid) p.wait() if p.returncode != 0: raise RuntimeError("iptables REJECT规则配置失败") # 4. 执行ip6tables规则 cmd = f"ip6tables -A OUTPUT -m owner --gid-owner {group_name} -j REJECT" p = subprocess.Popen(shlex.split(cmd), preexec_fn=os.setsid) p.wait() if p.returncode != 0: raise RuntimeError("ip6tables规则配置失败") # 5. 执行sg启动脚本 cmd = f"sg {group_name} './run.sh {HOST}:{PORT}'" p = subprocess.Popen(shlex.split(cmd), cwd=cwd, preexec_fn=os.setsid)
方案2:显式调用bash处理多命令
如果你希望保留单串多命令的写法,可以显式将命令传给bash -c,既不用开启shell=True,也能让shell帮你处理多命令分隔逻辑:
import os import subprocess HOST = "localhost" PORT = 8000 group_name = "no-external-internet" cmd_str = f"addgroup --system {group_name}; " \ f"iptables -A OUTPUT -m owner --gid-owner {group_name} -d {HOST} -p tcp --sport {PORT} -j ACCEPT; " \ f"iptables -A OUTPUT -m owner --gid-owner {group_name} -j REJECT; " \ f"ip6tables -A OUTPUT -m owner --gid-owner {group_name} -j REJECT; " \ f"sg {group_name} './run.sh {HOST}:{PORT}'" subprocess.Popen( ['bash', '-c', cmd_str], cwd="some directory", preexec_fn=os.setsid, )
注意:如果cmd_str中包含用户可控的输入参数,需要提前做参数校验,避免命令注入风险。
内容的提问来源于stack exchange,提问作者EzyHoo
相关产品推荐
相关产品推荐

