Table of Contents

About

Given a scenario where services are placed behind an Airport router that leases IP addresses dynamically from an ISP which in-turn are managed by CloudFlare one would wish to be able to update the CloudFlare A record to point to the new address once the ISP leases a different IP address to the Airport router. This project presents a script based on SNMP that accesses both the Apple Airport router and CloudFlare in order to update the IP address.

Diagram

Requirements

Setup

Download the script in the code section, configure the script by editing the parameters in the configuration section at the top and then place it in, say /etc/cron/cron.hourly. The script is created such that it will not return any output in case the CloudFlare name already resolves to the Airport external address. However, the script will announce when the IP has been updated or in case there are any errors in attempting to do so.

Code

airport-cloudflare.sh
#!/bin/sh
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3      ##
##  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  ##
##  rights of fair usage, the disclaimer and warranty conditions.        ##
###########################################################################
 
# This script sets a zone's A record on CloudFlare to point to an Airport 
# basestation's external IP address and is meant to be run regularly. 
#
# Requires a complete installation of SNMP along with relevant MIBs.
# On Debian, the packages: snmp, snmp-mibs-downloader must be installed.
# Airport MIB file must be downloaded from Apple and installed accordingly.
 
###########################################################################
##                            CONFIGURATION                              ##
###########################################################################
 
# Airport IP or hostname
AIRPORT_LOCAL_ADDRESS='192.168.0.1'
# Set this to the cloudflare zone name.
DOMAIN='slap.com'
# Set this to the A record name.
RECORD_NAME='forward.slap.com'
# Set this to your account e-mail address.
EMAIL='mongoose@whatever.tld'
# Set this to your CloudFlare API key.
API_KEY='1d10ab0dd09abfda6cc3dd0a92ca75de75671'
 
###########################################################################
##                               INTERNALS                               ##
###########################################################################
 
AIRPORT_EXTERNAL_ADDRRESS=`snmpwalk \
    -v1 \
    -c public $AIRPORT_LOCAL_ADDRESS \
    -m AIRPORT-BASESTATION-3-MIB \
        RFC1213-MIB::ipAdEntAddr | \
        head -1 | \
        awk '{ print $4 }'`
 
#echo $AIRPORT_EXTERNAL_ADDRRESS
 
if [ -z $AIRPORT_EXTERNAL_ADDRRESS ]; then
    echo "Unable to retrieve Airport external address."
    echo "Please check that the Airport has SNMP enabled."
    exit 1
fi
 
FLARE=$(curl -s https://www.cloudflare.com/api_json.html \
    -d 'a=rec_load_all' \
    -d "tkn=$API_KEY" \
    -d "email=$EMAIL" \
    -d "z=$DOMAIN")
 
if [ -z $FLARE ]; then
    echo "Unable to retrieve CloudFlare zone."
    echo "Please check the API key."
    exit 1
fi
 
ZONE_ADDRESS=`echo $FLARE | sed -E "s/.*,\"name\":\"$RECORD_NAME\",[^}]*?,\"type\":\"A\",[^}]*?,\"content\":\"([0-9\.]+?)\",.*/\1/"`
if [ -z $ZONE_ADDRESS ]; then
    echo "Unable to retrieve the CloudFlare zone IP address."
    exit 1
fi
 
# if the airport external address matches the zone address terminate
if [ $AIRPORT_EXTERNAL_ADDRRESS = $ZONE_ADDRESS ]; then
    exit 0
fi
 
RECORD_ID=`echo $FLARE | sed -E "s/.*\"rec_id\":\"([0-9]+?)\",[^}]*?,\"name\":\"$RECORD_NAME\",[^}]*?,\"type\":\"A\",.*/\1/"`
if [ -z $RECORD_ID ]; then
    echo "Unable to retrieve CloudFlare record ID for the zone."
    exit 1
fi
 
FLARE=$(curl -s https://www.cloudflare.com/api_json.html \
    -d 'a=rec_edit' \
    -d "tkn=$API_KEY" \
    -d "id=$RECORD_ID" \
    -d "email=$EMAIL" \
    -d "z=$DOMAIN" \
    -d 'type=A' \
    -d "name=$RECORD_NAME" \
    -d "content=$AIRPORT_EXTERNAL_ADDRRESS" \
    -d 'service_mode=0' \
    -d 'ttl=1')
 
RESULT=`echo $FLARE | sed -E "s/.*,\"result\":\"([^\,]*?)\",.*/\1/"`
if [ $RESULT = "success" ]; then
    echo "$RECORD_NAME now points to $AIRPORT_EXTERNAL_ADDRRESS"
    exit 0
fi
exit 1