ISPConfig 3 Update a DNS 'A' Record programmatically

Discussion in 'General' started by sghazagh, Sep 6, 2019.

  1. sghazagh

    sghazagh Member

    Hi every one,
    Is there any way to update only one 'A' record of one DNS zone of one domain in ISPConfig 3 programmatically via shell or php script?

    I use one subdomain A record to point to my home server which has no Static IP.
    Home server uploads the last public IP to my ISPConfig server outside the home and I need to update the A record of my subdomain to always have my latest home IP address so I can use my subdomain all the time and it resolves to my home dynamic address.

    Is there anyway I can make a script in either php or shell that I can use cron to update that record in ISPConfig?

    Any help would be appreciated,
     
  2. till

    till Super Moderator Staff Member ISPConfig Developer

    Yes, you can do that with the ISPConfig remote API. You can find examples here in the forum and in the remote_client folder of the ISPConfig tar.gz file.
     
  3. sghazagh

    sghazagh Member

    Thanks till,
    Functions of ISPconfig are endless, what a great application...
    I could manage to update my record properly as per API sample function 'dns_a_update.php'

    Thank you
     
    ahrasis and till like this.
  4. Tuumke

    Tuumke Active Member

    I'm sorry to hijack this thread, sort of :)
    I started looking into this. What i'm wonder is, do i need to update the serial as well? Or does that happen automaticly? Since the documentation is talking about the serial.
    Code:
    dns_a_update($session_id, $client_id, $primary_id, $params);
    Description:
    Updates an IPv4 record if type is a.
    
    Input Variables:
    $session_id, $client_id, $primary_id, $params
    
    Parameters (in $params):
    server_id  (int(11))
    zone  (int(11))
    name  (varchar(64))
    type  (enum('a','aaaa','alias','cname','hinfo','mx''naptr','ns','ptr','rp','srv','txt'))
    data  (varchar(255))
    aux  (int(11))
    ttl  (int(11))
    active  (enum('n','y'))
    stamp  (timestamp)
    serial  (int(10))
    
    Output:
    Returns the number of affected rows.
     
  5. till

    till Super Moderator Staff Member ISPConfig Developer

    You have to update the serial of the zone yourself if I remember correctly.
     
  6. Tuumke

    Tuumke Active Member

    At least i found a way to read current settings of my A record including serial ;)
    Will put my sh script here when im done.
     
    Last edited: Dec 3, 2019
    ahrasis and till like this.
  7. Tuumke

    Tuumke Active Member

    Where do i check what is going wrong in the ISPConfig logs?
    Trying with:
    Code:
    params=$( jq -n \
            --arg j_server_id "$server_id" \
            --arg j_zone "$zone" \
            --arg j_name "$name" \
            --arg j_type "$type" \
            --arg j_data "$currentip" \
            --arg j_aux "$aux" \
            --arg j_ttl "$ttl" \
            --arg j_active "$active" \
            --arg j_stamp "$stamp" \
            --arg j_serial "$new_serial" \
            '{server_id: $j_server_id, zone: $j_zone, name: $j_name, type: $j_type, data: $j_data, aux: $j_aux, ttl: $j_ttl,  active: $j_active, stamp: $j_stamp, serial: $j_serial}' )
    
    result=`restCall dns_a_update "{\"session_id\": \"$session_id\",\"client_id\":\"1\",\"primary_id\":\"128\",\"params\":\"$params\"}" | jq -r ".response"`
    but all i get is 'false'

    nvm. Went with a single restCall and no jq pipeline. Got:
    {"code":"invalid_data","message":"The JSON data sent to the api is invalid","response":false}

    This is the json:
    Code:
    {
      "server_id": "1",
      "zone": "3",
      "name": "test",
      "type": "A",
      "data": "1.1.1.1",
      "aux": "0",
      "ttl": "300",
      "active": "Y",
      "stamp": "2019-12-02 18:04:46",
      "serial": "2019120301"
    }
    -edit-
    As per the documentation, also client_id, primary_id and... 1 more are given before the params
     
    Last edited: Dec 3, 2019
  8. Tuumke

    Tuumke Active Member

    This is my script so far which gives a 'false' as return when trying to update the record.
    So far, i can read the current record i want but i cannot modify it. There is something wrong with the JSON i send?
    Code:
    #!/usr/bin/env bash
    
    set -e
    debug="false"
    
    if [ $debug = "true" ];
    then
            echo "###### DEBUGGING IS ON ########"
    fi
    
    ## Get current IP address
    currentip=`curl -sSk https://watismijnip.info/ | sed -E 's/<[^>]*>//g'`
    if [ $debug = "true" ]; then
            echo "Current IP: $currentip"
    fi
    
    
    exportfile='currentdata.json'
    if [ -f $exportfile ]; then
            rm $exportfile
    fi
    
    
    ## Login settings
    remote_user='remoteuser'
    remote_password='remotepasssword'
    remote_url='https://domain.tld:8080/remote/json.php'
    
    
    # restCall method data
    restCall() {
        curl -sSk -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d "${2}" "${remote_url}?${1}"
    }
    
    # Log in
    session_id=`restCall login "{\"username\": \"${remote_user}\",\"password\": \"${remote_password}\"}" | jq -r '.response'`
    if [[ $isession == "false" ]]; then
        echo "Login failed!"
        exit 1
    else
        echo "Logged in. Session is: $session_id"
    fi
    
    ## Define current data for the A record
    currentdata=`restCall dns_a_get "{\"session_id\": \"$session_id\",\"primary_id\":\"128\"}" | jq -r ".response"`
    echo $currentdata > currentdata.json
    ## Define data needed for JSON put
    server_id=`jq -r ".server_id" $exportfile`
    zone=`jq -r ".zone" $exportfile`
    name=`jq -r ".name" $exportfile`
    type=`jq -r ".type" $exportfile`
    data=`jq -r ".data" $exportfile`
    aux=`jq -r ".aux" $exportfile`
    ttl=`jq -r ".ttl" $exportfile`
    active=`jq -r ".active" $exportfile`
    stamp=`jq -r ".stamp" $exportfile`
    declare serial=`jq -r ".serial" $exportfile`
    
    if [ $debug == "true" ]; then
            echo "Server_id: $server_id"
            echo "Zone: $zone"
            echo "Name: $name"
            echo "Type: $type"
            echo "Data: $data"
            echo "Aux: $aux"
            echo "TTL: $ttl"
            echo "Active: $active"
            echo "Stamp: $stamp"
            echo "Serial: $serial"
    fi
    
    declare -i serial_date=${serial:0:8}
    declare -i count=${serial:8:2}
    current_date=`date +%Y%m%d`
    
    if [ $debug == "true"  ]; then
            echo "Serial_date: $serial_date"
            echo "Current_date: $current_date"
            echo "Count: $count"
    fi
    
    if [ $serial_date -gt $current_date ]; then
            count=$(( count + 1 ))
            if [ $count > 99 ]; then
                    declare -i serial_date=`$(( $serial_date +1 ))`
                    echo "L42:$serial_date"
                    declare -i count=0
            fi
            count=`printf "%02d" "$count"`
            declare -i new_serial="$serial_date""$count"
    else
    # test
            declare -i new_serial="$current_date""01"
    fi
    
    if [ $debug == "true" ]; then
            echo "New_serial: $new_serial"
    fi
    
    params=$( jq -n \
            --arg j_server_id "$server_id" \
            --arg j_zone "$zone" \
            --arg j_name "$name" \
            --arg j_type "$type" \
            --arg j_data "$currentip" \
            --arg j_aux "$aux" \
            --arg j_ttl "$ttl" \
            --arg j_active "$active" \
            --arg j_stamp "$stamp" \
            --arg j_serial "$new_serial" \
            '{server_id: $j_server_id, zone: $j_zone, name: $j_name, type: $j_type, data: $j_data, aux: $j_aux, ttl: $j_ttl,  active: $j_active, stamp: $j_stamp, serial: $j_serial}' )
    
    #result=`restCall dns_a_update "{\"session_id\": \"$session_id\",\"client_id\":\"1\",\"primary_id\":\"128\",\"params\":\"$params\"}" | jq -r ".response"`
    
    restCall dns_a_update "{\"session_id\": \"$session_id\",\"client_id\":\"1\",\"primary_id\":\"128\",\"params\":\"$params\"}"
    
    echo $result
    
    
    echo $params
    
    ## Cleanup and log out
    if [ -f $exportfile ]; then
            rm $exportfile
    fi
    
    
    # Log out
    if [[ `restCall logout "{\"session_id\": \"$session_id\"}" |jq -r .response` == "true" ]]; then
        echo "Logout successful."
        exit 0
    else
        echo "Logout failed!"
        exit 1
    fi
    
    
     
  9. Tuumke

    Tuumke Active Member

    Okai, i managed to update my record via bash script :)
    Need to build in a check to see if record IP address and current IP address are different or not :)
     
  10. ahrasis

    ahrasis Well-Known Member HowtoForge Supporter

  11. dan-v

    dan-v Member

    Many of the scripts in the remote_client folder instantiate an object of class "SoapClient", if I understand the scipts correctly. But none of them define the class. Does anybody know whrer the "SoapClient" class definition can be found ?
     
  12. till

    till Super Moderator Staff Member ISPConfig Developer

    That's an internal PHP function. You must install the PHP SOAP extension.
     
  13. variable99

    variable99 Active Member

    I don't know why ISPC staff does not publish PHP wrapper for API... LLM age allows that to create within 20 minutes. Here is working in production wrapper for ISPC SOAP API. Just rename it from .txt to .php (forum upload file filter does not allow .php files...).

    And sequence of updating DNS A record is like this:

    PHP:
    // get Zones (usually it is 1 zone per user)
            
    $dns_zones $this->panelServer
                
    ->with(
                    [
                        
    'client_id' => $ispc_user_id,
                        
    'server_id' => // ns1 server ID in ISPC if it is setup as master -> slave
                    
    ]
                )
                ->
    getDnsZonesByUser()
                ->
    response();

            
    $allDNSZones json_decode($dns_zonesTRUE);

            
    $allDNSZonesEnd end($allDNSZones);

            
    //get records from the zone
            
    $zoneRecords $this->panelServer
                
    ->with(
                    [
                        
    'zone_id' => $allDNSZonesEnd['id'],
                    ]
                )
                ->
    getDnsRRAllByZone()
                ->
    response();

            
    $dnsRecords json_decode($zoneRecordsTRUE);

            
    // delete existing records
            
    foreach ($dnsRecords_filtered ?: [] as $dr) {

                
    $recordToDelete $this->panelServer
                    
    ->with(
                        
    $dr
                    
    )
                    ->
    deleteDnsRecord()
                    ->
    response();

                
    $deletedRecord json_decode($recordToDeleteTRUE);

                if (!isset(
    $deletedRecord['result'])) {
                    continue;
                }
            }

            
    // add new records
            
    foreach ($ajax['records'] ?: [] as $r) {

                
    $valid_data $r;

                
    // add record
                
    $newDNSRecord $this->panelServer
                    
    ->with(
                        [
                            
    'server_id' => 1,   // DNS server ID
                            
    'zone' => $allDNSZonesEnd['id'],
                            
    'name' => $valid_data['record'],
                            
    'type' => $valid_data['type'],
                            
    'data' => $valid_data['val'],
                            
    'aux' => isset($valid_data['priority']) ? $valid_data['priority'] : 0,
                            
    // priority
                            
    'ttl' => $valid_data['ttl'],
                            
    'active' => 'y',
                            
    'stamp' => date('Y-m-d H:i:s'time()),
                            
    'serial' => NULL
                        
    ]
                    )
                    ->
    addDnsRecord()
                    ->
    response();
            }
    DO NOT edit serial manually. It is ISPC responsibility to do that.
     

    Attached Files:

  14. till

    till Super Moderator Staff Member ISPConfig Developer

    Then feel free to write it and provide it for the community.
     
    variable99 likes this.
  15. variable99

    variable99 Active Member

    I did that and attached to my previous response. Feel free to offer it for anyone in need.
     
    till likes this.
  16. dan-v

    dan-v Member

    Thanks Till. I did, and this got me past the first hurdle. Next, I find that I cannot even login to the API interface. The code which fails (which is straight from the remote_client folder of the ISPConfig tar.gz file, modified for the login specifics) is :
    Code:
    $username = 'RemoteUserForDNSFunctions';                           # created through ISPConfig panel
    $password = 'MyRemoteUserPassword';                                # anonymized
    $soap_location = 'https://localhost:8080/remote/index.php';
    $soap_uri = 'https://localhost:8080/remote/';
    
    $client = new SoapClient(null, array('location' => $soap_location,
            'uri'      => $soap_uri,
            'trace' => 1,
            'exceptions' => 1));
    
    
    try {
        if($session_id = $client->login($username, $password)) {
            echo 'Logged successfull. Session ID:'.$session_id.'<br />';
        }
    
    .......
    
    Is there anything obviously wrong there ?
    Thanks in advance for your help
     
    Last edited: Mar 8, 2026 at 2:08 AM
  17. till

    till Super Moderator Staff Member ISPConfig Developer

    Have you createda remote user in ISPConfig? The API login is not the normal login; it's a separate login that you create under System > remote users.

    And does your ISPConfig GUI has a valid SSL certificate, e.g., from Let's Encrypt? If not, you might have to disable the certificate check in PHP soap client connect function, there is a parameter for that.
     
  18. dan-v

    dan-v Member

    Yes I have, Till, and the username/password I used to try and login are those which I set-up using the ISPConfig panel. That does not seem to be the problem.

    I had to beat the bush a little to prove it but, yes, the SSL certificate seems to be the problem. I worked around it by substituting 'server.mydomain.com:8080' (which has a Let's Encrypt certificate) for 'localhost:8080' and it works. What bafffles me is why a certificate is required for a 'localhost:8080' access, and how to create one. (Yes, I have found how do disable the SSL check, but that is hardly a clean solution).

    But thanks for the tip
     
  19. dan-v

    dan-v Member

    I have another question, though: where do you find the definition of all the soap functions ? Per the online tutorial
    These should be found in:
    /usr/local/ispconfig/interface/lib/classes/remoting.inc.php​
    but they do not seem to be there. In particular, I can't find there the definition/parameters for the 2 functions which I aim at using :
    dns_a_get() and dns_a_update()​
    Where are these definitions located ?

    Thx

     
  20. till

    till Super Moderator Staff Member ISPConfig Developer

    The easiest way to find a function in a code base is to use the search function of your coding editor. Download the ISPConfig sources, open the project in a code editor like VSCode, and use its search function. You will see that the functions are in files in the folder /usr/local/ispconfig/interface/lib/classes/remote.d/ now, as there were too many to have them all in a single file.
     

Share This Page