• If you are still using CentOS 7.9, it's time to convert to Alma 8 with the free centos2alma tool by Plesk or Plesk Migrator. Please let us know your experiences or concerns in this thread:
    CentOS2Alma discussion

Removing FTP-user through API results in 500 Internal Server Error (PHP)

Jelle_Timmer

Basic Pleskian
Hi,

To automate some tasks for some projects I'm working on, I need to be able to automatically create or delete ftp-users.

Creating and listing ftp-accounts is working great, but when it comes to deleting them I run into an internal server error. Before my script gets the response it's waiting for, the script crashes.

- When live watching the Apache error-log with 'tail -f', the terminal-session is exited with 'Killed (core dumped)'
- Apache error-log shows:
Code:
(104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server, referer: http://example.com/ftp.php?action=delete
Premature end of script headers: ftp.php, referer: http://example.com/ftp.php?action=delete
- Webbrowser shows default '500 Internal Server Error'

I have checked and changed time-out settings, but it didn't help. Error occurs a few seconds after executing, while the timeouts are set to 60 to 90 seconds.

Packet send to Plesk:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<packet version="1.6.6.0">
  <ftp-user>
    <del>
      <filter>
        <name>test</name>
      </filter>
    </del>
  </ftp-user>
</packet>

As far as I know, my xml-packet is correctly formatted (API manual), which could be verified by the fact that the user I was trying to delete is actually deleted. But the overall behavior is far from satisfying.

Using secret key or username/password combination makes no difference, both result in error.

Running the same packet through the python-script referred to on this page also kills the terminal-session.

My files:

PleskApiClient.php
PHP:
<?php
/**
* @copyright 1999-2015. Parallels IP Holdings GmbH. All Rights Reserved.
*/

class PleskApiClient
{
    private $_host;
    private $_port;
    private $_protocol;
    private $_login;
    private $_password;
    private $_secretKey;

    private $result;

    /**
     * Create client
     *
     * @param Logging $log
     * @param string $host
     * @param int $port
     * @param string $protocol
     */
    public function __construct($host = 'localhost', $port = 8443, $protocol = 'https')
    {
        $this->_host = $host;
        $this->_port = $port;
        $this->_protocol = $protocol;
    }
 
    /**
     * @ignore
     */
    final function __set($var, $val) {
        $this->$var = $val;
    }

    /**
     * @ignore
     */
    final function __get($var) {
        return $this->$var;
    }

    /**
     * Setup credentials for authentication
     *
     * @param string $login
     * @param string $password
     */
    public function setCredentials($login, $password)
    {
        $this->_login = $login;
        $this->_password = $password;
    }

    /**
     * Define secret key for alternative authentication
     *
     * @param string $secretKey
     */
    public function setSecretKey($secretKey)
    {
        $this->_secretKey = $secretKey;
    }

    /**
     * Perform API request
     *
     * @param string $request
     */
    public function request($request)
    {
        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, "$this->_protocol://$this->_host:$this->_port/enterprise/control/agent.php");
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->_getHeaders());
        curl_setopt($curl, CURLOPT_POSTFIELDS, $request);

        $result = curl_exec($curl);

        curl_close($curl);

        return $result;
    }

    /**
     * Retrieve list of headers needed for request
     *
     * @return array
     */
    private function _getHeaders()
    {
        $headers = array(
            "Content-Type: text/xml",
            "HTTP_PRETTY_PRINT: TRUE",
        );

        if ($this->_secretKey) {
            $headers[] = "KEY: $this->_secretKey";
        } else {
            $headers[] = "HTTP_AUTH_LOGIN: $this->_login";
            $headers[] = "HTTP_AUTH_PASSWD: $this->_password";
        }

        return $headers;
    }
}

ftp.php (only for testing-purposes, will be integrated in existing product when working)
PHP:
<?php

require_once("PleskApiClient.php");

$_plesk = new PleskApiClient();
$_plesk->setSecretKey("79af145c-fd2d-dbe8-ecc7-33324d079de8");

function listFtpUsers() {
    $xml = new DomDocument("1.0", "UTF-8");
    $xml->formatOutput = true;
 
    $packet = $xml->createElement("packet");
    $packet->setAttribute("version", "1.6.6.0");
    $xml->appendChild($packet);
 
    $ftp = $xml->createElement("ftp-user");
    $packet->appendChild($ftp);
 
    $get = $xml->createElement("get");
    $ftp->appendChild($get);
 
    $get->appendChild($xml->createElement("filter"));
 
    return $xml;
}

function createFtpUser($user, $password, $domain) {
    $xml = new DomDocument("1.0", "UTF-8");
    $xml->formatOutput = true;
 
    $packet = $xml->createElement("packet");
    $packet->setAttribute("version", "1.6.6.0");
    $xml->appendChild($packet);
 
    $ftp = $xml->createElement("ftp-user");
    $packet->appendChild($ftp);
 
    $add = $xml->createElement("add");
    $ftp->appendChild($add);
 
    $add->appendChild($xml->createElement("name", $user));
    $add->appendChild($xml->createElement("password", $password));
    $add->appendChild($xml->createElement("webspace-name", $domain));

    return $xml;
}

function deleteFtpUser($user) {
    $xml = new DomDocument("1.0", "UTF-8");
    $xml->formatOutput = true;
 
    $packet = $xml->createElement("packet");
    $packet->setAttribute("version", "1.6.6.0");
    $xml->appendChild($packet);
 
    $ftp = $xml->createElement("ftp-user");
    $packet->appendChild($ftp);
 
    $del = $xml->createElement("del");
    $ftp->appendChild($del);
 
    $filter = $xml->createElement("filter");
    $del->appendChild($filter);
 
    $name = $xml->createElement("name", $user);
    $filter->appendChild($name);
 
    return $xml;
}
?>

<html>
<head>
<title>Plesk API FTP-users tests</title>

<body>

<input type="button" name="create" id="user_list" value="List users" />
<input type="button" name="create" id="user_create" value="Create user" />
<input type="button" name="delete" id="user_delete" value="Delete user" />

<?php
if(isset($_GET["action"]) && $_GET["action"] != "") {
    if($_GET["action"] == "create") {
        $createUserPacket = createFtpUser("test", "password", "example.com");
        echo($createUserPacket->saveXML());

        echo $_plesk->request($createUserPacket->saveXML());
    } elseif($_GET["action"] == "delete") {
        $deleteUserPacket = deleteFtpUser("test");
        echo($deleteUserPacket->saveXML());

        echo $_plesk->request($deleteUserPacket->saveXML());     
    } elseif($_GET["action"] == "list") {
        $listUserPacket = listFtpUsers();
        echo($listUserPacket->saveXML());
     
        echo $_plesk->request($listUserPacket->saveXML());
    }
}
?>

<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
    $("#user_list").on("click", function() {
        location.href = "<?php echo($_SERVER["PHP_SELF"]); ?>?action=list";
    });
 
    $("#user_create").on("click", function() {
        location.href = "<?php echo($_SERVER["PHP_SELF"]); ?>?action=create";
    });
 
    $("#user_delete").on("click", function() {
        location.href = "<?php echo($_SERVER["PHP_SELF"]); ?>?action=delete";
    });
});

</script>

</body>
</html>

Did I miss something?

I'm working with PHP 5.3 (FastCGI) on Plesk 12 on CentOS 6.6 x64.
 
Do you have SELinux enabled? Check it with

# getenforce
 
Try to fix it with

Code:
# chcon system_u:object_r:httpd_suexec_exec_t:s0 /usr/sbin/suexec
 
Done, but didn't help.

Before we're going to dig deep into the system: when Google-searching for issues possibly related to SELinux, there are a lot of hits about why it should be disabled all the way, but also a lot why it shouldn't.

What is your advice when it comes to SELinux in combination with Plesk?
 
To check if SELinux has something to do with this, I completely disabled it, restarted the server and ran the delete-ftp-user-script again. But the problems described in my first post all still exist.
 
Please confirm that all works fine with disabled SELinux?
 
Do you have any related errors in /var/log/httpd/error_log ?
 
Hi Igor,

We have 2 other servers running Plesk 12. I've tested creating and removing ftp-users through API on both and both servers are experiencing the same problems.

All servers are based on the same hardware-configuration (Virtual Private Server with full administrator-rights):
2 Intel Xeon cores with 4GB RAM and 150GB SSD-storage. On all servers Plesk is installed by using a pre-installed image with CentOS 6.6 and Plesk. This image is provided by the ISP.

Do you have some more options to try to get this working or is it something the Parallels-developers need to look into?

If you need some other information, please let me know.
 
I can only recommend to create a request to support team to do in-depth investigation to find the reason and to fix it. Please create a ticket to support at https://www.odin.com/support/request/ .
You may have free support, please check what kind of Plesk license you use for available support options at http://kb.odin.com/en/121580 .
If there’s no free support in your case, you can order Plesk per-incident support at http://www.odin.com/support/buy-support/ Support team will contact you as soon as purchase is processed, and they will do the best to resolve it.
If it is found that your problem was caused by product bug w/o available solution or workaround from Parallels, then your purchase will be re-funded.
 
Before spending money on a support case I spent some more time on trial-&-error coding and testing.

Turns out the strange behavior occurs when removing a ftp-user which belongs to the same site as the site which is executing the request.

So, if my API-request is executed from example.com/ftp.php and sends a request to delete the ftp-user 'test' at site 'example.com', the user is deleted and the execution of the script is terminated.

I'm not sure why this sudden termination of script-execution occurs, but I guess it has to do with Plesk needing to restart all processes owned by the site-user to finish the completion of the API-request.

Still some questions remain:
- Is there a way to prevent this 'resetting' behavior?
- why does this only occur when I remove a user and not when I add a user (since that is also a substantial change to the site-settings)?
 
Graceful restart for Apache doesn't help, but by moving the API-actions to a site/subdomain which is only used for API-communication I can bypass the problems.
 
Back
Top