2

I have a PHP script that takes a long time to load. We have moved our sites to a new server which lives behind a cloud load balancer. The maximum time limit for the load balancer is 120 seconds, but the script takes well over 5 minutes. Splitting the script up is not an option.

I rewrote the script so it would run on the command line, and I can successfully call this:

php -f /path/to/long_php.php > /path/to/log_file.php

I can then call this from PHP by doing this:

exec('php -f /path/to/long_php.php > /path/to/log_file.php');

Of course, because this script takes a long time to load, I don't want the PHP page to wait. Back on the command line I successfully used the at command like so:

echo "php -f /path/to/long_php.php > /path/to/log_file.php" | at now

So, I expected when running a similar thing in PHP it would work:

exec('echo "php -f /path/to/long_php.php > /path/to/log_file.php" | at now');

However this doesn't work. Unlike all the previou tries, the final command here give me SELinux errors:

----
type=SYSCALL msg=audit(07/25/2014 21:12:50.027:793672) : arch=x86_64 syscall=open success=no exit=-13(Permission denied) a0=7fd6fc2186bb a1=80000 a2=1b6 a3=0 items=0 ppid=55040 pid=55041 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=unix_chkpwd exe=/sbin/unix_chkpwd subj=unconfined_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(07/25/2014 21:12:50.027:793672) : avc:  denied  { read } for  pid=55041 comm=unix_chkpwd name=shadow dev=md2 ino=11797556 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file
----
type=SYSCALL msg=audit(07/25/2014 21:12:50.028:793673) : arch=x86_64 syscall=socket success=no exit=-13(Permission denied) a0=10 a1=3 a2=9 a3=7fff46547e40 items=0 ppid=55038 pid=55040 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=at exe=/usr/bin/at subj=unconfined_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(07/25/2014 21:12:50.028:793673) : avc:  denied  { create } for  pid=55040 comm=at scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=netlink_audit_socket
----
type=SYSCALL msg=audit(07/25/2014 21:12:50.028:793674) : arch=x86_64 syscall=socket success=no exit=-13(Permission denied) a0=10 a1=3 a2=9 a3=1 items=0 ppid=55038 pid=55040 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=at exe=/usr/bin/at subj=unconfined_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(07/25/2014 21:12:50.028:793674) : avc:  denied  { create } for  pid=55040 comm=at scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=netlink_audit_socket
----
type=SYSCALL msg=audit(07/25/2014 21:12:50.028:793675) : arch=x86_64 syscall=socket success=no exit=-13(Permission denied) a0=10 a1=3 a2=9 a3=7fff46547eb0 items=0 ppid=55038 pid=55040 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=at exe=/usr/bin/at subj=unconfined_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(07/25/2014 21:12:50.028:793675) : avc:  denied  { create } for  pid=55040 comm=at scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=netlink_audit_socket

Note: the above is from /var/log/audit/audit.log piped through ausearch -i

After this I piped those in to audit2allow -w to see why, and the first error was regarding allow_httpd_mod_auth_pam not being enabled. I then enabled this and got some other errors. Running them through audit2allow -M tmpat produced me tmpat.pp, with an associated tmpat.te:

module tmpat 1.0;

require {
        type initrc_var_run_t;
        type httpd_t;
        class capability audit_control;
        class file read;
}

#============= httpd_t ==============
allow httpd_t initrc_var_run_t:file read;
allow httpd_t self:capability audit_control;

Unfortunately, when I run semodule -i tmpat.pp it still doesn't work. Repeating the process of using audit2allow -w above gives me more of the same Missing type enforcement (TE) allow rule. errors. So with that in mind, I'm hoping someone can tell me what changes I need to make to the generated tmpat.te to make this work?

Note: I'm on CentOS 6.5, with Apache 2.2, PHP 5.4

Update:

After looking in to policies, albeit it being something new to me, and I came up with the following:

module tmpat 1.1;
  require {
  type tmp_t;
  type httpd_t;
  class file { create getattr open read rename unlink write };
  class dir { add_name getattr open read remove_name search write };
}

#============= httpd_t ==============
allow httpd_t tmp_t:file { create getattr open read rename unlink write };
allow httpd_t tmp_t:dir { add_name getattr open read remove_name search write };

Currently this compiles but won't install, giving me this:

[root@localhost tmp]# semodule -i tmpat
semodule:  Failed on tmpat!

...but hopefully this might give some insight in to what I'm trying to do.

hlovdal
  • 23,353
  • 10
  • 78
  • 148
LeonardChallis
  • 7,728
  • 5
  • 42
  • 72
  • 1
    SELinux is really the least of your problems here. Your long term processing needs to be handled by cron or some other backend process you schedule from the front. Instead you have created some processing script that has grown to large for real-time execution so you hacked it together by forking it to the background with exec('php -f /path/to/long_php.php > /path/to/log_file.php &'); – Alex Barker Jul 25 '14 at 22:29
  • Is using & the same as using at? I am using at after reading this page: http://symcbean.blogspot.co.uk/2010/02/php-and-long-running-processes.html. As for root, hmm, this is the standard Apache install on CentOS, using exec. whoami shows apache. – LeonardChallis Jul 25 '14 at 22:31
  • & is not the same as at but it acomplish the same thing you're requesting of | at now. The root thing was a miss read on the log. – Alex Barker Jul 25 '14 at 22:36
  • But the apache process can go away, which kills the long running php process as it's a child of it, right? Without SELinux this works perfectly. – LeonardChallis Jul 25 '14 at 22:38
  • It works perfectly without any security still doesn't make it a good solution. The front end Apache process is not where you should be doing this kind of maintenance processing. See http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5 You could try to nohup the process but I think you will run into the same kinds of problems with SELinux. You could probably add an exception, but if your doing that because of improperly deployed code, you may as well just turn it off all together. – Alex Barker Jul 25 '14 at 22:58
  • We want this to run only when an admin in our control panel instructs it, not on a regular basis. Admittedly there could be a cron job that runs every X minutes and will read some file/database value to see whether it should run it. Have you any information on why using at is a bad idea specifically? I'm aware of the cron manpage. – LeonardChallis Jul 25 '14 at 23:02

3 Answers3

2

did you ever come up with a solution to this problem? if so, i would like to know.

i came across the same situation, so i will post my solution here.

after configuring selinux to allow apache to execute the at command, i too came across a problem where no errors were found, but the at command not executing.

so i changed apache's shell config to /bin/sh (from /usr/sbin/nologin) and logged into a shell as user apache.

then executed atq to find out that the at command was properly executed from the apache process.

$ atq
46  Fri Nov 21 17:23:00 2014 a apache

but it was spooled forever and never executed. so i tried to execute an at job from the shell, and that worked no problem. and also to find out that the job that was spooled from apache had started to run also

$ atq
46  Fri Nov 21 17:23:00 2014 = apache

so i added a cron task that just keeps spooling empty at jobs (as user apache)

$ crontab -l
*/1 * * * * echo echo|/usr/bin/at now

it will cause a maximum 1 minute delay, but now at commands run from apache(php) exec.

i don't know the reason or the logic to this result, but it is a working solution for me.

CentOS 7.0.1406, Apache/2.4.6, PHP 5.4.16, Kernel 3.10.0-123.9.3.el7.x86_64

EDIT: i found out a simpler solution. this allows immediate at command execution.

# chcon -t unconfined_exec_t /sbin/httpd

see detailed document here

probably not a good idea if the server is shared by untrustful users

hc100
  • 146
  • 1
  • 6
0

You have to compile your policy to insert it with semanage.

First you use audit2allow to create a .te file (that would be the file you have in your updated post). Then you compile your policy to insert it with audit2allow -M tmpat, this will give you a .pp file. This file can be inserted, but look at the right syntax here:

semodule -i tmpat.pp
Ezeyme
  • 21
  • 1
  • It is not clear that you import the pp file. You wrote: `semodule -i tmpat` Can you be sure that you're using the .pp file instead of the .te file? – Ezeyme Aug 21 '14 at 12:01
0

Environment CentOS 8, nginx, php-fpm

This solution worked for me

SELinux change to allow httpd to execute

setsebool -P httpd_ssi_exec 1

Policy on executable

Instead of compiling your own policy the executable can be made to match the httpd policy

chcon -t httpd_exec_t <path_to_executable_file> -R

From a security perspective probably not recommend if the executable can do any damage

Joel Davis
  • 472
  • 5
  • 11