From 4d197e2687e0f7be76102f2792c12cc917de2551 Mon Sep 17 00:00:00 2001 From: Rob Fowell Date: Sun, 22 Mar 2026 18:07:01 +0000 Subject: [PATCH] first commit --- README.md | 138 ++++++++++++++++++++ plesk-api-subscriptions.php | 208 +++++++++++++++++++++++++++++++ plesk-subscriptions.php | 242 ++++++++++++++++++++++++++++++++++++ 3 files changed, 588 insertions(+) create mode 100644 README.md create mode 100644 plesk-api-subscriptions.php create mode 100644 plesk-subscriptions.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..7dd26b0 --- /dev/null +++ b/README.md @@ -0,0 +1,138 @@ +# Plesk Subscription Retriever + +PHP script to connect to multiple Plesk servers via SSH and retrieve subscription information. + +## Requirements + +- PHP 7.4+ with SSH2 extension +- SSH access to Plesk servers (root or admin user) +- Plesk CLI tools installed on remote servers (default on Plesk) + +## Installation + +### Install PHP SSH2 Extension + +**Ubuntu/Debian:** +```bash +sudo apt-get install libssh2-1-dev +sudo pecl install ssh2 +sudo systemctl restart php +``` + +**CentOS/RHEL:** +```bash +sudo yum install libssh2-devel +sudo pecl install ssh2 +sudo systemctl restart php +``` + +**Verify installation:** +```bash +php -m | grep ssh2 +``` + +## Configuration + +Edit `plesk-subscriptions.php` and update the `$servers` array: + +```php +$servers = [ + 'Blue' => [ + 'host' => 'blue.your-domain.com', // Server hostname or IP + 'port' => 22, + 'username' => 'root', // SSH username + 'password' => 'your_secure_password', // SSH password + ], + // ... add other servers +]; +``` + +### Using SSH Keys (Recommended) + +For better security, use SSH key authentication: + +1. Generate SSH keys (if you don't have them): +```bash +ssh-keygen -t rsa -b 4096 +``` + +2. Copy public key to each Plesk server: +```bash +ssh-copy-id root@blue.your-domain.com +ssh-copy-id root@red.your-domain.com +ssh-copy-id root@purple.your-domain.com +ssh-copy-id root@orange.your-domain.com +``` + +3. Update config in `plesk-subscriptions.php`: +```php +'Blue' => [ + 'host' => 'blue.your-domain.com', + 'port' => 22, + 'username' => 'root', + 'pubkey_file' => '/home/rob/.ssh/id_rsa.pub', + 'privkey_file' => '/home/rob/.ssh/id_rsa', +], +``` + +## Usage + +```bash +php plesk-subscriptions.php +``` + +## Output + +The script generates `subscriptions.json` with the following structure: + +```json +{ + "generated_at": "2026-03-22T10:30:00+00:00", + "servers": [ + { + "server_name": "Blue", + "host": "blue.your-domain.com", + "status": "success", + "error": null, + "subscriptions": [ + { + "primary_domain": "example.com", + "additional_domains": ["shop.example.com"], + "aliases": ["www.example.com"] + } + ] + } + ], + "summary": { + "total_servers": 4, + "successful": 4, + "failed": 0, + "total_subscriptions": 15 + } +} +``` + +## Alternative: Plesk XML-RPC API + +If you prefer using the Plesk API instead of SSH, see `plesk-api-subscriptions.php`. + +## Troubleshooting + +### SSH2 extension not found +```bash +php -m | grep ssh2 +# If nothing shows, install the extension (see Installation section) +``` + +### Authentication failed +- Verify SSH credentials +- Check SSH key permissions: `chmod 600 ~/.ssh/id_rsa` +- Ensure SSH access is allowed on the Plesk server + +### Plesk command not found +- Ensure you're connecting as root or a user with Plesk CLI access +- Verify Plesk is installed at `/usr/local/psa` + +### Connection timeout +- Check firewall rules (port 22) +- Verify the server hostname/IP is correct diff --git a/plesk-api-subscriptions.php b/plesk-api-subscriptions.php new file mode 100644 index 0000000..270415e --- /dev/null +++ b/plesk-api-subscriptions.php @@ -0,0 +1,208 @@ + [ + 'host' => 'https://blue.example.com:8443', + 'username' => 'admin', + 'password' => 'your_password_here', + ], + 'Red' => [ + 'host' => 'https://red.example.com:8443', + 'username' => 'admin', + 'password' => 'your_password_here', + ], + 'Purple' => [ + 'host' => 'https://purple.example.com:8443', + 'username' => 'admin', + 'password' => 'your_password_here', + ], + 'Orange' => [ + 'host' => 'https://orange.example.com:8443', + 'username' => 'admin', + 'password' => 'your_password_here', + ], +]; + +// Output file +$outputFile = __DIR__ . '/subscriptions-api.json'; + +/** + * Make XML-RPC request to Plesk API + */ +function pleskRequest(string $host, string $username, string $password, string $packet): ?SimpleXMLElement +{ + $url = rtrim($host, '/') . '/enterprise/control/agent.php'; + + $headers = [ + 'Content-Type: text/xml', + 'HTTP_PRETTY_PRINT: true', + 'HTTP_AUTH_LOGIN: ' . $username, + 'HTTP_AUTH_PASSWD: ' . $password, + ]; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $packet); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Set to true in production with valid certs + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + if ($error || $httpCode !== 200) { + return null; + } + + return simplexml_load_string($response); +} + +/** + * Get subscriptions from a Plesk server via API + */ +function getSubscriptionsFromServer(string $name, array $config): array +{ + $result = [ + 'server_name' => $name, + 'host' => $config['host'], + 'status' => 'unknown', + 'error' => null, + 'subscriptions' => [], + ]; + + // Build XML packet to get all domains + $packet = ' + + + + + + + + + + + + +'; + + $xml = pleskRequest($config['host'], $config['username'], $config['password'], $packet); + + if (!$xml) { + $result['status'] = 'error'; + $result['error'] = 'Failed to connect to Plesk API'; + return $result; + } + + // Check for errors in response + if (isset($xml->system->status) && (string)$xml->system->status !== 'ok') { + $result['status'] = 'error'; + $result['error'] = isset($xml->system->errtext) + ? (string)$xml->system->errtext + : 'API error'; + return $result; + } + + $result['status'] = 'success'; + + // Parse domain information + if (isset($xml->domain)) { + foreach ($xml->domain as $domain) { + if (isset($domain->get_info->result->data)) { + $data = $domain->get_info->result->data; + + $subscription = [ + 'primary_domain' => isset($data->name) ? (string)$data->name : '', + 'status' => isset($data->gen_info->status) ? (string)$data->gen_info->status : '', + 'additional_domains' => [], + 'aliases' => [], + ]; + + // Get domain aliases + $aliasPacket = ' + + + + + ' . htmlspecialchars($subscription['primary_domain']) . ' + + + + + + +'; + + $aliasXml = pleskRequest($config['host'], $config['username'], $config['password'], $aliasPacket); + + if ($aliasXml && isset($aliasXml->domain->get_info->result->data->hosting->vhost_name)) { + foreach ($aliasXml->domain->get_info->result->data->hosting->vhost_name as $vhost) { + if ((string)$vhost !== $subscription['primary_domain']) { + $subscription['aliases'][] = (string)$vhost; + } + } + } + + $result['subscriptions'][] = $subscription; + } + } + } + + return $result; +} + +/** + * Main execution + */ +echo "Plesk Subscription Retriever (API Version)\n"; +echo "==========================================\n\n"; + +$allResults = [ + 'generated_at' => date('c'), + 'servers' => [], + 'summary' => [ + 'total_servers' => count($servers), + 'successful' => 0, + 'failed' => 0, + 'total_subscriptions' => 0, + ], +]; + +foreach ($servers as $serverName => $config) { + echo "Connecting to {$serverName} ({$config['host']})... "; + + $serverData = getSubscriptionsFromServer($serverName, $config); + $allResults['servers'][] = $serverData; + + if ($serverData['status'] === 'success') { + $count = count($serverData['subscriptions']); + echo "OK ({$count} subscriptions)\n"; + $allResults['summary']['successful']++; + $allResults['summary']['total_subscriptions'] += $count; + } else { + echo "FAILED: {$serverData['error']}\n"; + $allResults['summary']['failed']++; + } +} + +// Save to JSON file +$jsonOutput = json_encode($allResults, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); +file_put_contents($outputFile, $jsonOutput); + +echo "\n"; +echo "Summary:\n"; +echo " Successful: {$allResults['summary']['successful']}\n"; +echo " Failed: {$allResults['summary']['failed']}\n"; +echo " Total Subscriptions: {$allResults['summary']['total_subscriptions']}\n"; +echo "\nResults saved to: {$outputFile}\n"; diff --git a/plesk-subscriptions.php b/plesk-subscriptions.php new file mode 100644 index 0000000..89bd335 --- /dev/null +++ b/plesk-subscriptions.php @@ -0,0 +1,242 @@ + [ + 'host' => 'blue.example.com', + 'port' => 22, + 'username' => 'root', + // Use password authentication + 'password' => 'your_password_here', + // OR use private key authentication (uncomment and configure) + // 'pubkey_file' => '/path/to/id_rsa.pub', + // 'privkey_file' => '/path/to/id_rsa', + ], + 'Red' => [ + 'host' => 'red.example.com', + 'port' => 22, + 'username' => 'root', + 'password' => 'your_password_here', + ], + 'Purple' => [ + 'host' => 'purple.example.com', + 'port' => 22, + 'username' => 'root', + 'password' => 'your_password_here', + ], + 'Orange' => [ + 'host' => 'orange.example.com', + 'port' => 22, + 'username' => 'root', + 'password' => 'your_password_here', + ], +]; + +// Output file +$outputFile = __DIR__ . '/subscriptions.json'; + +/** + * Connect to a Plesk server via SSH and retrieve subscriptions + */ +function getSubscriptionsFromServer(string $name, array $config): array +{ + $result = [ + 'server_name' => $name, + 'host' => $config['host'], + 'status' => 'unknown', + 'error' => null, + 'subscriptions' => [], + ]; + + // Check if SSH2 extension is available + if (!extension_loaded('ssh2')) { + $result['status'] = 'error'; + $result['error'] = 'SSH2 PHP extension not installed'; + return $result; + } + + try { + // Establish SSH connection + $connection = @ssh2_connect($config['host'], $config['port']); + + if (!$connection) { + $result['status'] = 'error'; + $result['error'] = 'Failed to connect to server'; + return $result; + } + + // Authenticate + if (isset($config['pubkey_file']) && isset($config['privkey_file'])) { + $authenticated = @ssh2_auth_pubkey_file( + $connection, + $config['username'], + $config['pubkey_file'], + $config['privkey_file'] + ); + } else { + $authenticated = @ssh2_auth_password($connection, $config['username'], $config['password']); + } + + if (!$authenticated) { + $result['status'] = 'error'; + $result['error'] = 'Authentication failed'; + return $result; + } + + $result['status'] = 'connected'; + + // Execute Plesk CLI command to list all subscriptions + $stream = @ssh2_exec($connection, '/usr/local/psa/bin/domain --list'); + + if (!$stream) { + $result['status'] = 'error'; + $result['error'] = 'Failed to execute Plesk command'; + return $result; + } + + stream_set_blocking($stream, true); + $output = stream_get_contents($stream); + fclose($stream); + + if (empty($output)) { + $result['status'] = 'error'; + $result['error'] = 'No output from Plesk command'; + return $result; + } + + $result['status'] = 'success'; + + // Parse the domain list output + $domains = explode("\n", trim($output)); + + foreach ($domains as $domainLine) { + $domainLine = trim($domainLine); + if (empty($domainLine)) { + continue; + } + + $subscription = parseDomainLine($domainLine, $connection, $config['username']); + if ($subscription) { + $result['subscriptions'][] = $subscription; + } + } + + } catch (Exception $e) { + $result['status'] = 'error'; + $result['error'] = $e->getMessage(); + } + + return $result; +} + +/** + * Parse a single domain line and retrieve additional details + */ +function parseDomainLine(string $line, $connection, string $username): ?array +{ + // Expected format: domain-name.tld (or with additional info) + $domainName = trim($line); + + if (empty($domainName)) { + return null; + } + + $subscription = [ + 'primary_domain' => $domainName, + 'additional_domains' => [], + 'aliases' => [], + ]; + + // Get additional domains for this subscription + $additionalCmd = sprintf( + '/usr/local/psa/bin/domain --info %s --format=json', + escapeshellarg($domainName) + ); + + $stream = @ssh2_exec($connection, $additionalCmd); + if ($stream) { + stream_set_blocking($stream, true); + $infoOutput = stream_get_contents($stream); + fclose($stream); + + $infoData = json_decode($infoOutput, true); + if ($infoData) { + // Extract additional domains if available in the JSON output + if (isset($infoData['additionalDomains']) && is_array($infoData['additionalDomains'])) { + $subscription['additional_domains'] = $infoData['additionalDomains']; + } + if (isset($infoData['aliases']) && is_array($infoData['aliases'])) { + $subscription['aliases'] = $infoData['aliases']; + } + } + } + + // Alternative: Get domain aliases using Plesk CLI + $aliasCmd = sprintf( + '/usr/local/psa/bin/domain_alias --list %s', + escapeshellarg($domainName) + ); + + $stream = @ssh2_exec($connection, $aliasCmd); + if ($stream) { + stream_set_blocking($stream, true); + $aliasOutput = stream_get_contents($stream); + fclose($stream); + + $aliases = explode("\n", trim($aliasOutput)); + $subscription['aliases'] = array_filter(array_map('trim', $aliases)); + } + + return $subscription; +} + +/** + * Main execution + */ +echo "Plesk Subscription Retriever\n"; +echo "============================\n\n"; + +$allResults = [ + 'generated_at' => date('c'), + 'servers' => [], + 'summary' => [ + 'total_servers' => count($servers), + 'successful' => 0, + 'failed' => 0, + 'total_subscriptions' => 0, + ], +]; + +foreach ($servers as $serverName => $config) { + echo "Connecting to {$serverName} ({$config['host']})... "; + + $serverData = getSubscriptionsFromServer($serverName, $config); + $allResults['servers'][] = $serverData; + + if ($serverData['status'] === 'success') { + $count = count($serverData['subscriptions']); + echo "OK ({$count} subscriptions)\n"; + $allResults['summary']['successful']++; + $allResults['summary']['total_subscriptions'] += $count; + } else { + echo "FAILED: {$serverData['error']}\n"; + $allResults['summary']['failed']++; + } +} + +// Save to JSON file +$jsonOutput = json_encode($allResults, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); +file_put_contents($outputFile, $jsonOutput); + +echo "\n"; +echo "Summary:\n"; +echo " Successful: {$allResults['summary']['successful']}\n"; +echo " Failed: {$allResults['summary']['failed']}\n"; +echo " Total Subscriptions: {$allResults['summary']['total_subscriptions']}\n"; +echo "\nResults saved to: {$outputFile}\n";