Configure OVH DDNS in OpenWRT

OpenWRT allows you to choose from a list of providers and configure DDNS pretty quickly. Router periodically checks your WAN IP, compares it to the A-record for the DDNS domain and updates it if necessary. The update happens via URL with a few parameters in it. However, at this moment DDNS option cannot be enabled for my domain purchased from Canadian OVH branch. The option is missing in the online graphical interface and API says that it is not available:

However, with access to REST API it’s possible to work around this limitation. First, create a sub-domain you wish to use for DDNS, e.g. vpn.mydomain.com.

Next, create API keys here. If you cannot login on that page it probably means that you’re have an account with some other OVH branch but not the one in Canada. In this case you might need to use the address appropriate for your location and modify API entry points in the script.

Request the following rights for API keys:

PUT for /domain/zone/yourwebsite.com/record/your_record_id
and
POST for /domain/zone/yourwebsite.com/refresh

Upload the bash script to /etc/ddns directory of your OpenWRT router:

#!/bin/sh
# Script to use dynamic DNS with OVH subdomain.
# Create API keys here:
# https://ca.api.ovh.com/createToken/
#
OVH_CONSUMER_KEY="your_consumerkey"
OVH_APP_KEY="your_app_key"
OVH_APP_SECRET="your_app_secret"
OVH_ZONE_NAME="yourwebsite.com"
# Record ID is a unique identifier for every entry in DNS zone.
# To get all records for a given zone: /domain/zone/{zoneName}/record#GET
# To get information regarding a specific record: POST /domain/zone/{zoneName}/refresh
# Normally there is just a handful of records so check them all to find the one that corresponds
# to the A record for the subdomaine that you wish to use for DDNS.
# Login to https://ca.api.ovh.com and then get the list of record for a zone.
OVH_RECORD_ID="1234567"
OVH_DDNS_SUBDOMAINE="vpn"

# API request to update A record:
HTTP_METHOD="PUT"
HTTP_QUERY="https://ca.api.ovh.com/1.0/domain/zone/$OVH_ZONE_NAME/record/$OVH_RECORD_ID"

# IP is passed from $__IP variable. This is being handled by OpenWRT.
current_dyn_ip=$__IP
ttl="60"

HTTP_BODY={"\"subDomain\"":" \"$OVH_DDNS_SUBDOMAINE\""," \"target\"":" \"$current_dyn_ip\""," \"ttl\"":" $ttl"}
TIME=$(curl -s https://ca.api.ovh.com/1.0/auth/time)
CLEAR_SIGN=$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME

# Calculate hash to sign the request
SIG='$1$'$(printf "%s" "$CLEAR_SIGN" | sha1sum | awk '{print $1}' )

API_ANSWER=""
API_ANSWER=$(curl -sS -X $HTTP_METHOD \
-H "Content-Type: application/json" \
-H "X-Ovh-Application: $OVH_APP_KEY" \
-H "X-Ovh-Timestamp: $TIME" \
-H "X-Ovh-Signature: $SIG" \
-H "X-Ovh-Consumer: $OVH_CONSUMER_KEY" \
$HTTP_QUERY --data "$HTTP_BODY") 

# Write to log file if API call was succesful
if [ $API_ANSWER=null ]; then
 write_log 7 "API query to update A-record was successful"
else
 write_log 7 "Error. Query to update A-record failed. OVH API server responded: $API_ANSWER"
fi

# Second API call to refresh DNS zone. Otherwise A-record update won't be taken into account:
HTTP_METHOD="POST"                                                                               
HTTP_QUERY="https://ca.api.ovh.com/1.0/domain/zone/$OVH_ZONE_NAME/refresh" 

HTTP_BODY=""
TIME=$(curl -s https://ca.api.ovh.com/1.0/auth/time)                                                           
CLEAR_SIGN=$OVH_APP_SECRET+$OVH_CONSUMER_KEY+$HTTP_METHOD+$HTTP_QUERY+$HTTP_BODY+$TIME                         
                                                                                                               
SIG='$1$'$(printf "%s" "$CLEAR_SIGN" | sha1sum | awk '{print $1}' )                                            

# pause before the second API call
sleep 2
                                                                                                               
API_ANSWER=$(curl -sS -X $HTTP_METHOD \
-H "Content-Type: application/json" \
-H "X-Ovh-Application: $OVH_APP_KEY" \
-H "X-Ovh-Timestamp: $TIME" \
-H "X-Ovh-Signature: $SIG" \
-H "X-Ovh-Consumer: $OVH_CONSUMER_KEY" \
$HTTP_QUERY --data "$HTTP_BODY")

# Write to log file if API call was succesful                                                              
if [ $API_ANSWER=null ]; then                                                                                  
 write_log 7 "API query to refresh DNS zone was successful"                                                      
else                                                                                                           
 write_log 7 "Error. Query to refresh DNS zone failed. OVH API server responded: $API_ANSWER"                  
fi 

return 0

Update variables at the beginning of the script with values for your account and sub-domain. Make the script executable and prevent it from being deleted during the next system upgrade with the following commands:

root@OpenWrt:~# chmod +x /etc/ddns/update_ovh.sh
root@OpenWrt:~# echo /etc/ddns/update_ovh.sh >>/etc/sysupgrade.conf

Next, install “curl”, “coreutils-sha1sum” and “luci-app-ddns” packages via System => Software menu in OpenWRT. Check out the official guide on OpenWRT website.

On the DDNS configuration page select “– custom –” from the list of providers and specify the path to the script and lookup hostname. Don’t forget to select the checkbox next to “Enable” option at the top of the page. You don’t need to specify anything in “Domain”, “Username”, “Password” and other fields on that page:

Start DDNS service:

Check logs for Dynamic DNS ( Services => Dynamic DNS => Edit => Log File Viewer) to make sure there are no errors.

2 thoughts on “Configure OVH DDNS in OpenWRT

  1. Oh, my! That’s exactly what I’ve found after I received the answer for my support ticket from OVH. And exactrly for OpenWRT! )

    Thank you!

    By the way, OpenWRT 21.02 seems to lack write_log command. Replaced it with logger call.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.