Python <тип 'exceptions.OSError'>: нет устройств pty при запуске из apache / php - PullRequest
0 голосов
/ 18 мая 2019

Я пытался выполнить скрипт Python, используя pexpect на CentOS с Apache от вызова php, но получил ошибку «out of pty devices» только при попытке запустить его из браузера.Если я выполняю код с python или даже php, он работает нормально.

Версия CentOS - 7.5.1804

Версия Python - 2.7.5

Apache / 2.4.6(CentOS)

Я перемонтировал / dev / pts, как предлагалось в нескольких постах с gid 5, добавил пользователя apache в ttl (или gid 5 как бы) и проверил унаследованное членство, протестированное с пользователем apache, выполнил тот же код на коробке Debian без проблем.

$ mount | grep pts
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)

В моей последней попытке я попытался настроить его для cgi, чтобы обойти php, в случае, если это было преступником.Это не было.

Версия, использующая cgi

#!/usr/bin/env python
import pexpect, pickle, cgi, cgitb


cgitb.enable()

print "Content-type: text/html\n\n"

def load_obj(name):
    with open(name + '.pkl', 'rb') as f:
        return pickle.load(f)


def test(query):
    user = load_obj('pw')
    password = user['password']
    user = user['user']
    capture = {}
    host = query["host"]
    connection = pexpect.spawn("ssh %s@%s" % (user, query['host']))
    connection.timeout = 90
    connection.expect('.assword:|\?')
    print "It worked!"
    connection.close()

if __name__ == '__main__':
    query = {"host": "x.x.x.x"}
    test(query)

Версия, использующая выполнение php / shell

#!/usr/bin/env python
import pexpect, pickle


def load_obj(name):
    with open(name + '.pkl', 'rb') as f:
        return pickle.load(f)


def test(query):
    user = load_obj('pw')
    password = user['password']
    user = user['user']
    capture = {}
    host = query["host"]
    connection = pexpect.spawn("ssh %s@%s" % (user, query['host']))
    connection.timeout = 90
    connection.expect('.assword:|\?')
    print "It worked!"
    connection.close()

if __name__ == '__main__':
    query = {"host": "x.x.x.x"}
    try:
        test(query)
    except Exception as e:
        print "Fail: " + str(e)

PHP скрипт

<?php
ini_set("display_errors", TRUE);
$output = shell_exec("python /var/www/html/include/test.py");
$output = nl2br("$output");
echo $output;
?>

Работа сshell:

$ python /var/www/html/cgi-enabled/temp.py
It worked!

Работа с PHP с использованием оболочки:

$ php test.php 
It worked!<br />

Не работает с CGI:

--> -->


<type 'exceptions.OSError'> Python 2.7.5: /usr/bin/python
Fri May 17 21:39:56 2019
A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.

 /var/www/html/cgi-enabled/temp.py in ()
     24     connection.close()
     25 
     26 if __name__ == '__main__':
     27     query = {"host": "x.x.x.x"}
=>   28     test(query)
test = <function test>, query = {'host': 'x.x.x.x'}
 /var/www/html/cgi-enabled/temp.py in test(query={'host': 'x.x.x.x'})
     18     capture = {}
     19     host = query["host"]
=>   20     connection = pexpect.spawn("ssh %s@%s" % (user, query['host']))
     21     connection.timeout = 90
     22     connection.expect('.assword:|\?')
connection undefined, global pexpect = <module 'pexpect' from '/usr/lib/python2.7/site-packages/pexpect/__init__.pyc'>, pexpect.spawn = <class 'pexpect.pty_spawn.spawn'>, user = 'user', query = {'host': 'x.x.x.x'}
 /usr/lib/python2.7/site-packages/pexpect/pty_spawn.py in __init__(self=<pexpect.pty_spawn.spawn object>, command='ssh user@x.x.x.x', args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None, ignore_sighup=False, echo=True, preexec_fn=None, encoding=None, codec_errors='strict', dimensions=None, use_poll=False)
    202             self.name = '<pexpect factory incomplete>'
    203         else:
=>  204             self._spawn(command, args, preexec_fn, dimensions)
    205         self.use_poll = use_poll
    206 
self = <pexpect.pty_spawn.spawn object>, self._spawn = <bound method spawn._spawn of <pexpect.pty_spawn.spawn object>>, command = 'ssh user@x.x.x.x', args = [], preexec_fn = None, dimensions = None
 /usr/lib/python2.7/site-packages/pexpect/pty_spawn.py in _spawn(self=<pexpect.pty_spawn.spawn object>, command='ssh user@x.x.x.x', args=[], preexec_fn=None, dimensions=None)
    301 
    302         self.ptyproc = self._spawnpty(self.args, env=self.env,
=>  303                                      cwd=self.cwd, **kwargs)
    304 
    305         self.pid = self.ptyproc.pid
cwd undefined, self = <pexpect.pty_spawn.spawn object>, self.cwd = None, kwargs = {'echo': True, 'preexec_fn': None}
 /usr/lib/python2.7/site-packages/pexpect/pty_spawn.py in _spawnpty(self=<pexpect.pty_spawn.spawn object>, args=['/usr/bin/ssh', 'user@x.x.x.x'], **kwargs={'cwd': None, 'echo': True, 'env': None, 'preexec_fn': None})
    312     def _spawnpty(self, args, **kwargs):
    313         '''Spawn a pty and return an instance of PtyProcess.'''
=>  314         return ptyprocess.PtyProcess.spawn(args, **kwargs)
    315 
    316     def close(self, force=True):
global ptyprocess = <module 'ptyprocess' from '/usr/lib/python2.7/site-packages/ptyprocess/__init__.pyc'>, ptyprocess.PtyProcess = <class 'ptyprocess.ptyprocess.PtyProcess'>, ptyprocess.PtyProcess.spawn = <bound method type.spawn of <class 'ptyprocess.ptyprocess.PtyProcess'>>, args = ['/usr/bin/ssh', 'user@x.x.x.x'], kwargs = {'cwd': None, 'echo': True, 'env': None, 'preexec_fn': None}
 /usr/lib/python2.7/site-packages/ptyprocess/ptyprocess.py in spawn(cls=<class 'ptyprocess.ptyprocess.PtyProcess'>, argv=['/usr/bin/ssh', 'user@x.x.x.x'], cwd=None, env=None, echo=True, preexec_fn=None, dimensions=(24, 80))
    224 
    225         if use_native_pty_fork:
=>  226             pid, fd = pty.fork()
    227         else:
    228             # Use internal fork_pty, for Solaris
pid undefined, fd undefined, global pty = <module 'pty' from '/usr/lib64/python2.7/pty.pyc'>, pty.fork = <function fork>
 /usr/lib64/python2.7/pty.py in fork()
    105         return pid, fd
    106 
=>  107     master_fd, slave_fd = openpty()
    108     pid = os.fork()
    109     if pid == CHILD:
master_fd undefined, slave_fd undefined, global openpty = <function openpty>
 /usr/lib64/python2.7/pty.py in openpty()
     27     except (AttributeError, OSError):
     28         pass
=>   29     master_fd, slave_name = _open_terminal()
     30     slave_fd = slave_open(slave_name)
     31     return master_fd, slave_fd
master_fd undefined, slave_name undefined, global _open_terminal = <function _open_terminal>
 /usr/lib64/python2.7/pty.py in _open_terminal()
     68                 continue
     69             return (fd, '/dev/tty' + x + y)
=>   70     raise os.error, 'out of pty devices'
     71 
     72 def slave_open(tty_name):
global os = <module 'os' from '/usr/lib64/python2.7/os.pyc'>, os.error = <type 'exceptions.OSError'>
<type 'exceptions.OSError'>: out of pty devices 
      args = ('out of pty devices',) 
      errno = None 
      filename = None 
      message = 'out of pty devices' 
      strerror = None

Не работает с помощью браузера + скрипт php:

Fail: out of pty devices

Редактировать:

Проведя дополнительные исследования, я обнаружил, что SELinux был причиной невозможности создания pty.Тем не менее, я не смог создать правила, чтобы обойти это, и был в состоянии выполнить сценарий, только если я делаю это в разрешающем режиме.Делая это, я все еще получаю журналы от монитора событий, но эти журналы для генерации разрешений с помощью audit2allow были неудачны в их реализации.

$ sudo grep denied /var/log/audit/audit.log 
type=AVC msg=audit(1558274289.811:55236): avc:  denied  { open } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file
type=AVC msg=audit(1558274289.812:55237): avc:  denied  { ioctl } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file
type=AVC msg=audit(1558274289.813:55238): avc:  denied  { getattr } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file

Я следовал этому руководству, которое все еще не решало проблему.отказы: https://osric.com/chris/accidental-developer/2017/11/selinux-audit2why-audit2allow-policy-files/

$ sudo audit2why -i /var/log/audit/audit.log
type=AVC msg=audit(1558274289.811:55236): avc:  denied  { open } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file

        Was caused by:
                Missing type enforcement (TE) allow rule.

                You can use audit2allow to generate a loadable module to allow this access.

type=AVC msg=audit(1558274289.812:55237): avc:  denied  { ioctl } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file

        Was caused by:
                Missing type enforcement (TE) allow rule.

                You can use audit2allow to generate a loadable module to allow this access.

type=AVC msg=audit(1558274289.813:55238): avc:  denied  { getattr } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file

        Was caused by:
                Missing type enforcement (TE) allow rule.

                You can use audit2allow to generate a loadable module to allow this access.

[bkapsch@ohcolm-pl-nettlmn01 test]$ sudo audit2allow -i /var/log/audit/audit.log


#============= httpd_sys_script_t ==============
allow httpd_sys_script_t ptmx_t:chr_file { getattr ioctl open };

$ sudo audit2allow -i /var/log/audit/audit.log --module local > local.te
$ make -f /usr/share/selinux/devel/Makefile local.pp
/usr/share/selinux/devel/include/contrib/container.if:14: Error: duplicate definition of container_runtime_domtrans(). Original definition on 14.
/usr/share/selinux/devel/include/contrib/container.if:33: Error: duplicate definition of container_runtime_exec(). Original definition on 60.
/usr/share/selinux/devel/include/contrib/container.if:52: Error: duplicate definition of container_search_lib(). Original definition on 97.
/usr/share/selinux/devel/include/contrib/container.if:71: Error: duplicate definition of container_exec_lib(). Original definition on 116.
/usr/share/selinux/devel/include/contrib/container.if:90: Error: duplicate definition of container_read_lib_files(). Original definition on 135.
/usr/share/selinux/devel/include/contrib/container.if:109: Error: duplicate definition of container_read_share_files(). Original definition on 154.
/usr/share/selinux/devel/include/contrib/container.if:131: Error: duplicate definition of container_exec_share_files(). Original definition on 176.
/usr/share/selinux/devel/include/contrib/container.if:149: Error: duplicate definition of container_manage_lib_files(). Original definition on 194.
/usr/share/selinux/devel/include/contrib/container.if:169: Error: duplicate definition of container_manage_lib_dirs(). Original definition on 251.
/usr/share/selinux/devel/include/contrib/container.if:205: Error: duplicate definition of container_lib_filetrans(). Original definition on 287.
/usr/share/selinux/devel/include/contrib/container.if:223: Error: duplicate definition of container_read_pid_files(). Original definition on 305.
/usr/share/selinux/devel/include/contrib/container.if:242: Error: duplicate definition of container_systemctl(). Original definition on 324.
/usr/share/selinux/devel/include/contrib/container.if:267: Error: duplicate definition of container_rw_sem(). Original definition on 349.
/usr/share/selinux/devel/include/contrib/container.if:285: Error: duplicate definition of container_use_ptys(). Original definition on 367.
/usr/share/selinux/devel/include/contrib/container.if:303: Error: duplicate definition of container_filetrans_named_content(). Original definition on 385.
/usr/share/selinux/devel/include/contrib/container.if:336: Error: duplicate definition of container_stream_connect(). Original definition on 432.
/usr/share/selinux/devel/include/contrib/container.if:355: Error: duplicate definition of container_spc_stream_connect(). Original definition on 453.
/usr/share/selinux/devel/include/contrib/container.if:376: Error: duplicate definition of container_admin(). Original definition on 474.
/usr/share/selinux/devel/include/contrib/container.if:423: Error: duplicate definition of container_spc_read_state(). Original definition on 659.
/usr/share/selinux/devel/include/contrib/container.if:441: Error: duplicate definition of container_auth_domtrans(). Original definition on 521.
/usr/share/selinux/devel/include/contrib/container.if:460: Error: duplicate definition of container_auth_exec(). Original definition on 540.
/usr/share/selinux/devel/include/contrib/container.if:479: Error: duplicate definition of container_auth_stream_connect(). Original definition on 559.
/usr/share/selinux/devel/include/contrib/container.if:498: Error: duplicate definition of container_runtime_typebounds(). Original definition on 578.
Compiling targeted local module
/usr/bin/checkmodule:  loading policy configuration from tmp/local.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 19) to tmp/local.mod
Creating targeted local.pp policy package
rm tmp/local.mod.fc tmp/local.mod
$ sudo semodule -i local.pp
$ sudo grep denied /var/log/audit/audit.log 
type=AVC msg=audit(1558274289.811:55236): avc:  denied  { open } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file
type=AVC msg=audit(1558274289.812:55237): avc:  denied  { ioctl } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file
type=AVC msg=audit(1558274289.813:55238): avc:  denied  { getattr } for  pid=16235 comm="python" path="/dev/ptmx" dev="devtmpfs" ino=7590 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file
type=AVC msg=audit(1558275000.559:55293): avc:  denied  { getattr } for  pid=16310 comm="python" name="/" dev="devpts" ino=1 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=filesystem
type=AVC msg=audit(1558275000.560:55294): avc:  denied  { getattr } for  pid=16310 comm="python" path="/dev/pts/5" dev="devpts" ino=8 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=chr_file
type=AVC msg=audit(1558275000.560:55295): avc:  denied  { read write } for  pid=16310 comm="python" name="5" dev="devpts" ino=8 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=chr_file
type=AVC msg=audit(1558275000.560:55295): avc:  denied  { open } for  pid=16310 comm="python" path="/dev/pts/5" dev="devpts" ino=8 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=chr_file
type=AVC msg=audit(1558275000.562:55296): avc:  denied  { ioctl } for  pid=16311 comm="python" path="/dev/pts/5" dev="devpts" ino=8 scontext=system_u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=chr_file
...