• Please be aware: Kaspersky Anti-Virus has been deprecated
    With the upgrade to Plesk Obsidian 18.0.64, "Kaspersky Anti-Virus for Servers" will be automatically removed from the servers it is installed on. We recommend that you migrate to Sophos Anti-Virus for Servers.
  • The Horde webmail has been deprecated. Its complete removal is scheduled for April 2025. For details and recommended actions, see the Feature and Deprecation Plan.
  • We’re working on enhancing the Monitoring feature in Plesk, and we could really use your expertise! If you’re open to sharing your experiences with server and website monitoring or providing feedback, we’d love to have a one-hour online meeting with you.

Issue Running PHP script as a scheduled task

QWeb Ric

Basic Pleskian
This seems to be an issue that's well documented, but I've crawled through existing threads for hours and can't seem to find a solution that works for me.

In a nutshell, I have a fresh Plesk 17.8.11 running on a fresh CentOS 7.4.1708, and I can't seem to get scheduled PHP scripts to run.

There's only one subscription at the moment. It's set up with "Access to the server over SSH" set to "/bin/bash (chrooted)".

Tools & Settings -> Scheduled Tasks -> Settings is currently set to have the Crontab shell as /bin/bash . Have tried /bin/sh which my other CentOS 6.9 servers use. I can't remember what the default was now... possibly /bin/bash (chrooted).

Finally, I have the scheduled task set up, like this:

cd /var/www/vhosts/mydomain.co.uk/httpdocs/scripts; php -f myscript.php

But either leaving the task to run on schedule via cron, or using the "run now" buttons, I'm getting the following errors:

-: line 0: cd: /var/www/vhosts/mydomain.co.uk/httpdocs/scripts: No such file or directory
-: php: command not found

I realise Plesk gives the "Run PHP script" option instead of setting up as a command, but the script itself relies on being run as a command (it uses things like the getcwd() function) and being able to load in modules that are shared by the website. Basically, I need to do it the "Run as command" way.

I run other servers with the same Plesk version and have no issues on those. This is the only server that's CentOS 7 though, and it's been years since I configured the others so perhaps I've forgotten something important.

Any thoughts please?
 
Bit of an update:

Seems that if I set both "Service plan" -> "SSH access to the server" AND "Domains" -> "Hosting Settings" to "/bin/bash", the scheduled task works, even if "Tools & Settings" -> "Scheduled Tasks" -> "Settings" -> "Crontab shell" set to "/bin/bash (chrooted)".

But if either the service plan or the individual domains hosting settings are set to "/bin/bash (chroooted)", the cron fails, even if the crontab shell is set to /bin/bash.

On my CentOS 6.9 servers I have all service plans and subscription domains set to "/bin/bash (chrooted)", and this is important for me as we only allow SFTP connections, not FTP/FTPS. Creating a subscription that therefore allows /bin/bash shell seems dangerous!

I'm wondering if there's a bug in Plesk 17.8.11 that's causing the crontab shell setting to be ignored?
 
AHA!

If I look in the generated crontab on this new server, for the cron set up under this subscription, I see the following:

# crontab -u subscriptionuser -l
MAILTO="[email protected]"
SHELL="/bin/bash"
0,30 * * * * cd /var/www/vhosts/thedomain.com/httpdocs/scripts; php -f thescript.php

If I do the same on one of my CentOS 6.9 servers, this SHELL line is missing

So it seems scheduled tasks created under Plesk 17.8.11 are being given this extra line which either doesn't get created on CentOS 6.9, or we just haven't created any crons on the older servers since Plesk was last updated.

If I switch the service plan and domain SSH access back to /bin/bash (chroot), this SHELL line changes to the following "/usr/local/psa/bin/chrootsh" and the cron fails.

The "Tools & Settings" -> "Scheduled Tasks" -> "Settings" -> "Crontab shell" setting has absolutely no effect on this line.
 
Still hoping for some help with this please. In a nutshell, the "Crontab shell" setting appears to have absolutely no affect because the generated crontab incorporates a "SHELL=" line that always matches the subscription's "Access over SSH" setting. This means that setting a subscription to /bin/bash (chroot) breaks PHP cron scripts because PHP isn't in he chrooted environment.

Although switching the subscription to allow SSH access to /bin/bash works, this isn't a long term solution because any developers who need SFTP access to the subscription website effectively end up with full SSH access to the server, rather than a chrooted session.
 
Although switching the subscription to allow SSH access to /bin/bash works, this isn't a long term solution because any developers who need SFTP access to the subscription website effectively end up with full SSH access to the server, rather than a chrooted session.
If Plesk allowed the execution of cron jobs that can circumvent the chrooted limit, such developers could easily use cron jobs to hack the server, too.
 
That's a fair point Peter, but we don't generally provide Plesk access to anybody who doesn't already have full server access anyway. We do however provide SFTP access to developers and my concern is that they could use these credentials to SSH in and receive full /bin/bash access.

On our other servers, which as far as I can tell only differ in that they're CentOS 6.9 rather than 7, we've not had any of the above issues.
 
Three years later, I've finally cracked it. For anybody stumbling onto this still looking for a fix, with the help of How to add programs to a chrooted shell environment template in Plesk this is what's finally working for me. I can now have chrooted SSH with working SFTP access and PHP cron scripts on CentOS 7:

Bash:
wget https://plesk.zendesk.com/hc/article_attachments/360023025260/update_chroot.zip
unzip update_chroot.zip
chmod +x update_chroot.sh
sudo ./update_chroot.sh --rebuild
sudo ./update_chroot.sh --add /opt/plesk/php/5.6/bin/php
sudo ./update_chroot.sh --add /opt/plesk/php/7.1/bin/php
sudo ./update_chroot.sh --add /opt/plesk/php/7.2/bin/php
sudo ./update_chroot.sh --add /opt/plesk/php/7.3/bin/php
sudo mkdir /var/www/vhosts/chroot/usr/share
sudo cp -a /usr/share/zoneinfo /var/www/vhosts/chroot/usr/share/zoneinfo
for i in /opt/plesk/php/5.6/lib64/php/modules/*.so; do sudo ./update_chroot.sh --add $i; done
for i in /opt/plesk/php/7.1/lib64/php/modules/*.so; do sudo ./update_chroot.sh --add $i; done
for i in /opt/plesk/php/7.2/lib64/php/modules/*.so; do sudo ./update_chroot.sh --add $i; done
for i in /opt/plesk/php/7.3/lib64/php/modules/*.so; do sudo ./update_chroot.sh --add $i; done
sudo cp -a /opt/plesk/php/5.6/etc /var/www/vhosts/chroot/opt/plesk/php/5.6/
sudo cp -a /opt/plesk/php/7.1/etc /var/www/vhosts/chroot/opt/plesk/php/7.1/
sudo cp -a /opt/plesk/php/7.2/etc /var/www/vhosts/chroot/opt/plesk/php/7.2/
sudo cp -a /opt/plesk/php/7.3/etc /var/www/vhosts/chroot/opt/plesk/php/7.3/
sudo ./update_chroot.sh --devices tty
sudo ./update_chroot.sh --add ssh
sudo ./update_chroot.sh --add /usr/libexec/openssh/sftp-server
    # (Might be worth checking /usr/libexec/openssh/sftp-server is correct. Ref subsystem= line in /etc/ssh/sshd_config)
sudo ./update_chroot.sh --apply all
 
Actually, it turns out this still isn't enough for some PHP crons to work. Scripts using PHP's copy() command to copy remote files fail, and scripts connecting to MySQL using localhost instead of 127.0.0.1 fail too. I'm sure there are other similar issues.
 
For localhost you may need to bind/mount the mysqld.sock, since localhost uses a unix socket.

Copy() won't be able to copy files outside the chroot - that would break the purpose of chroot
 
For localhost you may need to bind/mount the mysqld.sock, since localhost uses a unix socket.

Copy() won't be able to copy files outside the chroot - that would break the purpose of chroot
Thanks John. Any idea how I'd do the binding please?

Ref copy() - I think PHP wraps Curl into all file read functions, like copy(), so that you can open http URLs with them. I've noticed SoapClient functions fail too so I think it's probably that I need to add curl and maybe wget in to the chroot environment for these functions to work.

Problem is, we have a number of Cron jobs doing all sorts of complicated tasks and I'm worried that if we now switch domains over to chrooted environments, some of these functions are going to stop working and figuring out exactly what needs adding to the chroot is going to be near impossible.

Weirdly though - the Cron settings have always been set to /bin/bash chrooted, so I don't really understand why setting the domain to chrooted too would affect them.
 
mount --bind -o <mount/bind options> /path/file /path/to/bind/to

This is non persistent. To make it persist w/ reboots, and an entry to /etc/fstab

Either way. PHP cannot access files outside it's chroot. If it could be simply circumvented by a copy(), chroot would be pointless. How did you set the domain to /bin/ash chrooted? Is the domain's PHP fpm running via chroot=/?
 
PHP is set to run as an FPM app under Apache and regardless of whether the domain is set to chrooted or not, this seems to work just fine.

It's the "access to the server over ssh" setting under the domains hosting settings that I want to change to /bin/bash chrooted. Until today, doing this broke SFTP access and the ability for Cron to run PHP scripts at all. The above bash lines fix both those issues but it seems various PHP functions still fail.

Within Tools & Settings -> scheduled tasks -> settings, the crontab shell is already set to chrooted. This has always been the case and crons have worked just fine with this set, as long as the domains access over ssh is set to /bin/bash and not chrooted.

On older CentOS 6.9 servers, everything just worked with the domains set to chrooted and PHP scripts set up as Cron jobs. In fact, a lot of the websites on this CentOS 7 server were migrated from 6.9.
 
Everything you want to access needs to be in the chroot. Files, libraries, shared objects, bin/executables, and their dependencies. If an executable can access the main filesystem, there would obviously be security concerns.

Therefore, if you want to access file x in the chroot, you need to copy it to the chroot skeleton, or bind mount it. There's no alternative.
 
Perhaps this is better explained by example.

Attached is one of our cron scripts (I've had to zip it up to attach here but it's just a 39 line PHP script). You can ignore the require lines at the top - those just set up some variables that all scripts rely on basically. So this is effectively the entire script in this case. Note the copy() on line 10, which grabs the content of the url defined in the $source variable and drops it into a local file location in ../required. This particular script is in a sub directory so ../ is accessible within the chroot.

With the domain set to chrooted ssh access, this cron fails at line 10 and 25, spitting out both of those error messages on lines 11 and 26. With the domain set to regular /bin/bash ssh access but crons still set to chrooted, everything works fine.

The cron itself is this:

Bash:
cd /httpdocs/scripts; /opt/plesk/php/7.3/bin/php -f refresh-ip2location-files.php

The only difference being that it's `cd /httpdocs` when chrooted, and `cd /var/www/vhosts/foobah.com/httpdocs` when not chrooted.
 

Attachments

  • refresh-ip2location-files.zip
    567 bytes · Views: 1
I see - in this case yes, you will need the cron binary.

In the future, you can ltrace/strace the process to see which function calls are failing and why,
 
Do you mean the curl binary? So `./update_chroot.sh --add curl` ?

Perhaps there's a way to just list out all of the programs PHP depends on and --add those. Although, surely the `for` loops in my earlier post should have done this actually...
 
Not exactly. Technically not supposed to disclose this, but I believe our implementation runs via cp /usr/lib64/*.so* to ..../skeleton/usr/lib64/

The shared object you're looking for is libcurl.so. Copying all the shared objects should fix *most* of what PHP needs at runtime.
 
Last edited:
Interesting...

So, this should do it then?

Bash:
sudo cp -a /usr/lib64/*.so* /var/www/vhosts/chroot/usr/lib64/
 
Back
Top