ISPconfig API Funktion um alle DNS-zonen abzurufen?

typoworx

Member
Hallo zusammen,
ich möchte zwecks umfangreicherer Umstellungen ein Skript entwickeln um alle in IspConfig verwalteten DNS-zonen zu Cloudflare zu migrieren. Hierzu habe ich mir das Cloudflare-SDK für PHP geholt und teste gerade verschiedene Packagist-Pakete um einen Client für die SoapApi von IspConfig nutzen zu können. Das Paket "guru-digital/ispconfig-remote-api" (https://github.com/guru-digital/ispconfig-remote-api) scheint grundsätzlich zu funktionieren.

Ich habe aber noch keinen Weg gefunden aus IspConfig mittels SOAP alle DNS-Zonen abzurufen. Die Methode dns_zone_get verlangt immer explizit nach einer Zone-Id. Ich habe dies ebenfalls im Quelltext von IspConfig überprüft (siehe: https://git.ispconfig.org/ispconfig/ispconfig3/blob/master/interface/lib/classes/remote.d/dns.inc.php).

Mir ist bei der Sichtung des Quelltext aufgefallen, dass es scheinbar auch einen JSON-Handler gibt (https://git.ispconfig.org/ispconfig/ispconfig3/blob/master/interface/web/remote/json.php) weiß jemand wie man über den ggf. Daten ziehen kann für die DNS-Zonen?

Gibt es einen anderen Weg um die Liste abzufragen über die API-Schnittstelle? Es sei noch erwähnt, dass nicht alle DNS-Zonen zwingend als Web- oder Mail-Domain konfiguriert sind. Eine Abfrage hierüber (falls dies überhaupt vorgesehen ist) dürfte daher auch nicht in Frage kommen.

Ich freue mich auf Tipps.
 

typoworx

Member
Ok ich habe nun selbst eine Lösung gefunden:

PHP:
use GDM\ISPConfig as ISPconfig;

// This is optional required if using self-signed ssl-certs!
$context = stream_context_create([
    'ssl' => [
        // set some SSL/TLS specific options
        'verify_peer' => false,
        'verify_peer_name' => false,
        'allow_self_signed' => true
    ]
]);

$ispconfigAdapter = new ISPconfig\SoapClient(
    'https://server01.my-ispconfig-server.com:8080/remote/index.php',
    'api-username', 'my!super$safe!password',
    $context
);

// Fetch all ispConfig client IDs
$clients = $ispconfigAdapter->clientGetAll();

$dnsZones = [];
foreach($clients as $clientId)
{
    try
    {
        $response = $ispconfigAdapter->dnsZoneGetByUser($clientId, 1);
    }
    catch(\Exception $e)
    {
    }

    if(!empty($response))
    {
        // Fetch all client's dns-zones
        $dnsZones = array_merge($dnsZones, $response);
    }
}

dd($dnsZones);
 

typoworx

Member
Okay... ist leider auch wieder nur eine Teil-Lösung, weil nicht jede DNS-Zone explizit einer Client-ID zugeordnet ist :mad:

Hat noch jemand eine Idee außer direkt auf die MySQL-DB (die aktuell nicht Public zur Verfügung steht) alle DNS-Zonen zu erhalten? Kann doch nicht sein, dass eine API so ein feature nicht mal für einen "admin" API-User bereithält.
 

florian030

Active Member
Du musst doch nur ein Feld bei der Abfrage auf leer setzen. Meinetwegen auch restCall('dns_zone_get', array('session_id' => $session_id, 'primary_id' => array()));
 

typoworx

Member
Hallo,
habe ich bereits alles durch probiert. Es ist übrigens eine Soap und keine REST-API. Ich denke auch nicht, dass es an der Soap-Implementierung des Composer-Paket 'GDM\ISPConfig' liegt. Dort werden auch nur die Methoden implementiert, welche an die API weiter geleitet werden.

Für den API Aufruf um den es geht sieht das wie folgt aus:
PHP:
public function dnsZoneGet($primaryId)
{
    return $this->makeCall('dns_zone_get', $this->getSessionId(), $primaryId);
}

public function makeCall($function)
{
    $args = func_get_args();
    array_shift($args);
    try {
        // Hier wird die Anfrage an die Soap-Methode weitergeleitet!
        $result = call_user_func_array([$this->soapClient, $function], $args);
    } catch (\Exception $exc) {
        $result              = false;
        $this->lastException = $exc;
    }
    return $result;
}
Ich habe so ziemlich alle Varianten durch probiert:

dnsZoneGet('') => false
dnsZoneGet('%') => false
dnsZoneGet('*') => false
dnsZoneGet(-1) => false
dnsZoneGet([]) => false
dnsZoneGet(['origin' => '%']) => false
dnsZoneGet(['active' => 'Y']) => false
 

typoworx

Member
Siehe -> dnsZoneGet(['origin' => '%']) => false
Ist nur eine andere Schreibweise ( '[]' statt 'array()' ).

Wir reden aber schon noch von der Soap-API? Oder nutzt du diese Funktion irgendwo direkt in IspConfig? Bei mir klappt es wie gesagt leider nicht. Weiß auch noch nicht, wo es hakt - wenn es wirklich so funktionieren soll.

Bekomme nur "false" (boolean) zurück.
 

Till

Administrator
SOAP API Aufruf um alle aktiven Zonen zu erhalten:

$dns_zones = $client->dns_zone_get($session_id, -1);

Siehe zugrundeliegende Funktion getDataRecord in interface/lib/remoting_lib.inc.php

Die Variante von Florian mit dem % Platzhalter und Angabe einer Spalte geht auch.
 

typoworx

Member
Danke @Till. Dann muss bei dem Composer Paket wohl irgendwas Faul sein. habe aber noch nicht entdeckt wo es hakt. Ich werde mal direkt mit über SoapClient testen. Ich habe inzwischen auch die Code-Beispiele dazu entdeckt im ISPconfig-Repo.
 

typoworx

Member
Hallo @Till und @florian030,
danke noch mal für eure Unterstützung. Scheinbar hat das Composer-Paket GDM/Ispconfig tatsächlich einen Schuss.

Direkt über SoapClient klappt's einwandfrei, entsprechend der Beispiele im IspConfig-GIT!
(https://git.ispconfig.org/ispconfig/ispconfig3/blob/ispconfig-3.0.4/remoting_client/examples/)

PHP:
$client = new \SoapClient(null, [
    'location' => 'https://xxxxxxxxx.com:8080/remote/index.php',
    'uri'      => 'https://xxxxxxxxx.com:8080/remote/',
    'trace' => 1,
    'exceptions' => 1
]);

$session_id = $client->login('dnsapi', '****************');
dd($client->dns_zone_get($session_id, -1));
 

typoworx

Member
Falls jemand eine Lösung für Laravel oder Symfony sucht... ich habe mir jetzt selbst einen kleinen Client-Wrapper gebastelt:

PHP:
<?php
namespace App\Http\Api;

use SoapClient;
use Illuminate\Support\Facades\Cache;

/**
* Class IspConfig
* @package App\Http\Api
*/
class IspConfigClient
{
    /**
     * @var string
     */
    protected $sessionId = '';

    /**
     * @var \SoapClient
     */
    protected $soapClient;

    /**
     * @var \Exception|null
     */
    protected $lastException;

    /**
     * @var bool
     */
    protected $enableDebug = false;

    protected $cacheTTL = 30;  // Default 30 seconds

    protected $cacheBackend = 'file';


    /**
     * IspConfigClient constructor.
     * @param string $uri
     * @param null $streamContext
     * @param array $options
     */
    public function __construct(string $uri, $streamContext = null, array $options = [])
    {
        $options = array_merge(
            [
                'location' => sprintf('%s/remote/index.php', $uri),
                'uri'      => sprintf('%s/remote/', $uri),
                'trace' => false,
                'exceptions' => true,
                'stream_context' => $streamContext
            ],
            $options
        );

        $this->soapClient = new SoapClient(null, $options);
    }

    /**
     * @param $methodName
     * @param $arguments
     * @return mixed
     */
    public function __call($methodName, $arguments)
    {
        // Put Method-Name to Arguments
        array_unshift($arguments, $methodName);

        return call_user_func_array([$this, 'apiCall'], $arguments);
    }

    /**
     * Soap-Proxy into Cache-Facade
     * @param string $methodName
     * @return mixed
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function apiCallCached(string $methodName)
    {
        $cacheKey = sprintf('%s::%s', __CLASS__, $methodName);

        if(Cache::store($this->cacheBackend)->has($cacheKey))
        {
            $response = Cache::store($this->cacheBackend)->get($cacheKey);
        }
        else
        {
            $response = call_user_func_array([$this, 'apiCall'], func_get_args());

            if(!empty($response))
            {
                Cache::store($this->cacheBackend)->add($cacheKey, $response, $this->cacheTTL);
            }
        }

        return $response;
    }

    /**
     * Soap-Proxy without Caching
     * @param string $methodName
     * @return mixed|null
     * @throws \SoapFault
     */
    public function apiCall(string $methodName)
    {
        $this->lastException = null;

        try
        {
            // Camel-Case to underscore
            $methodName = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $methodName));

            $soapArguments = func_get_args();
            array_shift($soapArguments);

            if($methodName !== 'login')
            {
                array_unshift($soapArguments, $this->sessionId);
            }

            return call_user_func_array([$this->soapClient, $methodName], $soapArguments);
        }
        catch (\SoapFault $e)
        {
            $this->lastException = $e;

            if($this->enableDebug === true)
            {
                throw $e;
            }
        }

        return null;
    }

    public function enableDebug()
    {
        $this->enableDebug = true;
    }

    /**
     * @return bool
     */
    public function hasError() : bool
    {
        return $this->lastException instanceof \Exception;
    }

    /**
     * @return \Exception|null
     */
    public function getError()
    {
        return $this->lastException;
    }

#region Caching
    /**
     * @param int $ttl
     */
    public function setCacheTTL(int $ttl)
    {
        $this->cacheTTL = abs($ttl);
    }

    /**
     * @param string $cacheBackend
     */
    public function setCacheBackend(string $cacheBackend)
    {
        $this->cacheBackend = $cacheBackend;
    }

    /**
     * @param string $method
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function flush(string $method)
    {
        $cacheKey = __CLASS__.'::'.$method;
        Cache::store($this->cacheBackend)->delete($cacheKey);
    }

    public function flushAll()
    {
        Cache::store($this->cacheBackend)->clear();
    }
#endregion

#region API Calls
    /**
     * @param string $username
     * @param string $password
     * @return mixed|null
     * @throws \SoapFault
     */
    public function login(string $username, string $password)
    {
        $sessionId = $this->apiCall('login', $username, $password);
        $this->sessionId = $sessionId;

        return $sessionId;
    }

    /**
     * @param $primaryId
     * @return mixed|null
     * @throws \SoapFault
     */
    public function dnsZoneGet($primaryId)
    {
        return $this->apiCall(__FUNCTION__, func_get_args());
    }
#endregion
}
 

typoworx

Member
So habe mir auf Laravel-Basis ein Tool gebastelt mit dem ich meine ispConfig DNS-Zonen zu Cloudflare schieben kann (initiale migration). Werde demnächst evtl. mal schauen, ob man Cloudflare auch komplett bei IspConfig rein integriert bekommt in das DNS-Management :)
 

buerkli

New Member
Dies ist ein großartiger Beitrag, Ich kann die ispConfig-DNS-Zone nicht an Cloud flare senden, ich bin verärgert. Cloud flare taugt nix? Kann ich mir nicht vorstellen. Aber jetzt habe ich deine Hilfe!
 
Zuletzt bearbeitet:

Werbung

Top