├── .gitignore ├── .env.sample ├── docker-compose.yml ├── Dockerfile ├── Dockerfile.travisci ├── LICENSE ├── .travis.yml ├── README.md └── namecheap-ddns-update /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | DOMAIN=example.com 2 | SUBDOMAINS=abc,xyz 3 | MULTI_DOMAINS=example2.com=aaa,bbb:example3.com=ccc 4 | INTERVAL=600s 5 | NC_DDNS_PASS=example.com=1234,example2.com=4567,example3.com=1111 6 | #NC_DDNS_PASS=123456 7 | 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | namecheap-ddns: 4 | #build: . 5 | image: joshuamorris3/namecheap-ddns-update 6 | container_name: namecheap_ddns_updater 7 | restart: unless-stopped 8 | env_file: .env 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Start from apline, a minimal docker image 2 | FROM alpine:latest 3 | 4 | # Add in SSL certificates for use with https, curl to call the update endpoint, 5 | # bash used by the namecheap-ddns-update script, and gawk to parse the response 6 | RUN apk add --update ca-certificates curl bash gawk 7 | 8 | # Copy the pre-built go executable and the static files 9 | ADD namecheap-ddns-update / 10 | RUN chmod 744 /namecheap-ddns-update 11 | 12 | # This script registers subdomains to a domain you own and hosted by namecheap 13 | CMD ["/namecheap-ddns-update"] 14 | -------------------------------------------------------------------------------- /Dockerfile.travisci: -------------------------------------------------------------------------------- 1 | # Start from apline, a minimal docker image 2 | FROM --platform=$BUILDPLATFORM alpine:latest 3 | ARG TARGETPLATFORM 4 | ARG BUILDPLATFORM 5 | RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" 6 | 7 | # Add in SSL certificates for use with https, curl to call the update endpoint, 8 | # bash used by the namecheap-ddns-update script, and gawk to parse the response 9 | RUN apk add --update ca-certificates curl bash gawk 10 | 11 | # Copy the pre-built go executable and the static files 12 | ADD namecheap-ddns-update / 13 | RUN chmod 744 /namecheap-ddns-update 14 | 15 | # This script registers subdomains to a domain you own and hosted by namecheap 16 | CMD ["/namecheap-ddns-update"] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Josh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | os: linux 3 | language: shell 4 | 5 | services: 6 | - docker 7 | 8 | env: 9 | global: 10 | - DOCKER_PLATFORMS="linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/386,linux/arm/v7,linux/arm/v6" 11 | 12 | git: 13 | depth: 1 14 | 15 | before_install: 16 | # Registering file format recognizers since RUN command is used 17 | - sudo docker run --privileged linuxkit/binfmt:v0.7 18 | # Get scripts for building Docker images 19 | - id=$(docker create moikot/docker-tools) 20 | - docker cp $id:/scripts.sh /tmp/scripts.sh && docker rm -v $id 21 | # Update docker to the latest version and enable BuildKit 22 | - /tmp/scripts.sh update_docker 23 | 24 | stages: 25 | - name: build amd64 26 | if: branch != master AND tag IS blank 27 | - name: build and deploy master 28 | if: branch = master AND tag IS blank 29 | - name: build and deploy a version 30 | if: tag =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/ 31 | 32 | matrix: 33 | include: 34 | 35 | - stage: build amd64 36 | script: 37 | - >- 38 | /tmp/scripts.sh build_images 39 | ${TRAVIS_REPO_SLUG} ${TRAVIS_COMMIT::6} linux/amd64 --file Dockerfile.travisci 40 | - stage: build and deploy master 41 | script: 42 | - >- 43 | echo "${DOCKER_PASSWORD}" | 44 | docker login -u "${DOCKER_USERNAME}" --password-stdin 45 | - >- 46 | /tmp/scripts.sh build_images 47 | ${TRAVIS_REPO_SLUG} master ${DOCKER_PLATFORMS} --push --file Dockerfile.travisci 48 | - stage: build and deploy a version 49 | script: 50 | - >- 51 | echo "${DOCKER_PASSWORD}" | 52 | docker login -u "${DOCKER_USERNAME}" --password-stdin 53 | - >- 54 | /tmp/scripts.sh build_images 55 | ${TRAVIS_REPO_SLUG} ${TRAVIS_TAG} ${DOCKER_PLATFORMS} --push --file Dockerfile.travisci 56 | # Uploading the readme via the docker hub api is currently not supported 57 | # See https://github.com/docker/hub-feedback/issues/1927 58 | # - >- 59 | # - /tmp/scripts.sh push_readme 60 | # - ${TRAVIS_REPO_SLUG} README.md 61 | # - ${DOCKER_USERNAME} ${DOCKER_PASSWORD} 62 | 63 | notifications: 64 | email: false 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # namecheap-ddns-update 2 | Update the IP address of your [namecheap.com](namecheap.com) Dynamic DNS A records. 3 | 4 | [![Build Status](https://travis-ci.org/joshuamorris3/namecheap-ddns-update.svg?branch=master)](https://travis-ci.org/joshuamorris3/namecheap-ddns-update) 5 | 6 | ## Overview 7 | Use this to update the IP address of A records for one or more domains that are hosted by [namecheap.com](namecheap.com). If you created one or more A records using [namecheap.com](namecheap.com) Dynamic DNS, then this will update the IP address to: 8 | * The IP address you pass in as an argument using -i 9 | * Or, if the -i is omitted or left blank, the IP address seen by [namecheap.com](namecheap.com)'s servers when you run this script. If you run this from within your network, then the externally visible IP address is used by [namecheap.com](namecheap.com) to update the A record value. If you have a server with a public IP address, then this utility can be run from that server and -i can be omitted. 10 | 11 | ## Running it 12 | 13 | Check the help (-h) for details. The one argument that can only be set as an environment variable is NC_DDNS_PASS. The Dynamic DNS Password from [namecheap.com](namecheap.com)'s dashboard -> Advanced DNS page for the domain with the A records you want to update. 14 | 15 | The remaining arguments can be set in one of the following ways: 16 | 17 | 1. Pass them in on the command line 18 | 2. Set them as environment variables e.g. export DOMAIN=domain.tld, or DOMAIN=domain.tld ./namecheap-ddns-update .... 19 | 3. Create an environment file .env in the same directory as the script. This file is sourced if it is found and is readable e.g. source ./.env (the [.env.sample](.env.sample) provides an example) 20 | 4. Create an environment file called .namecheap-ddns-update, in the directory of the user running this script. This file is sourced if it is found and is readable e.g. source ~/.namecheap-ddns-update 21 | 22 | Basic usages is as follows: 23 | ``` 24 | Usage: namecheap-ddns-update [-h] [-e] [-d DOMAIN] [-s SUBDOMAINS] [-m MULTI_DOMAINS] [-i IP] [-t INTERVAL] 25 | 26 | Update the IP address of one or more domains subdomains that you own at 27 | namecheap.com. This can only update an existing A record, it cannot create 28 | a new A record. Use namecheap's advanced DNS settings for your domain to 29 | create A records. 30 | 31 | For details on how this works see: 32 | https://www.namecheap.com/support/knowledgebase/article.aspx/29/11/how-do-i-use-a-browser-to-dynamically-update-the-hosts-ip 33 | 34 | The args d, s, m, i and t have corresponding ENV options. The Dynamic DNS 35 | Password has to be set for each domain with the NC_DDNS_PASS environment 36 | variable. If there is one domain being updated, then the format of the 37 | NC_DNS_PASS value is the domain password. If you want to update multiple 38 | domains, then the value is a comma separated list of domain/password pairs. 39 | Example: NC_DNS_PASS=example.com=1a2s3d4f5g,example2.com=5g6h7j8k9l 40 | 41 | You could also create an environment file in the same directory as the script, 42 | called .env, or in directory of the user running this script, called 43 | .namecheap-ddns-update. The .env file is sourced first if found, if it does 44 | not exist, then .namecheap-ddns-update sourced if found. 45 | 46 | -h display this help and exit 47 | -e exit if any call to update a subdomains IP address fails 48 | -d DOMAIN the domain that has one or more SUBDOMAINS (A records) 49 | to update. DOMAIN/SUBDOMAINS can be used at the same 50 | time as MULTI_DOMAINS to update multiple domains 51 | -s SUBDOMAINS comma separated list of subdomains (A records) of DOMAIN 52 | to update. DOMAIN/SUBDOMAINS can be used at the same 53 | time as MULTI_DOMAINS to update multiple domains 54 | -m MULTI_DOMAINS other domains to combine with subdomains (A records). It 55 | can be specified multiple times on the command line, 56 | at the same time specified as the environment variable 57 | MULTI_DOMAINS. The format for the command line argument 58 | is: -m example.com=abc,xyz -m example2.com=def,ghi. The 59 | format for the environment variable is: 60 | MULTI_DOMAINS=example.com=abc,xyz:example2.com=def,ghi 61 | Each domain/subdomains pair is separated by a colon (:) 62 | -i IP IP address to set the subdomain(s) to. If blank namecheap 63 | will use the callers public IP address. 64 | -t INTERVAL set up a interval at which to run this. Uses bash sleep 65 | format e.g. NUMBER[SUFFIX] where SUFFIX can be, s for 66 | seconds (default), m for minutes, h for hours, d for days 67 | ``` 68 | 69 | ### With a known public IP 70 | This example would update the IP address to 127.0.0.1, for the two A records abc and xyz under the domain example.com e.g. abc.mydomain.com and xyz.example.com 71 | ``` 72 | ./namecheap-ddns-update -d example.com -s "abc,xyz" -i 127.0.0.1 73 | ``` 74 | ### I don't know my public IP 75 | This example runs every hour (1h) to update the IP address to the callers public IP address, for the two A records abc and xyz under the domain example.com e.g. abc.mydomain.com and xyz.example.com. This is useful when you sit behind an IP address that can change e.g. home internet service provider who dynamically assigns you a public IP address 76 | ``` 77 | ./namecheap-ddns-update -d example.com -s "abc,xyz" -t 1h 78 | ``` 79 | ### I have multiple domains 80 | This example updates multiple domains using the `-m` script argument. 81 | ``` 82 | ./namecheap-ddns-update -d example.com -s "abc,xyz" -m example2.com=aaa,bbb -m example3.com=ccc,ddd 83 | ``` 84 | or using the MULTI_DOMAINS environment variable 85 | ``` 86 | MULTI_DOMAINS=example4.com=eee,fff:example5.com=ggg,hhh ./namecheap-ddns-update -d example.com -s "abc,xyz" -m example2.com=aaa,bbb -m example3.com=ccc,ddd 87 | ``` 88 | ### Docker 89 | 90 | This docker container should be available from _joshuamorris3/namecheap-ddns-update_ 91 | 92 | ``` 93 | docker run -e "NC_DDNS_PASS=123456" -e "DOMAIN=example.com" -e "SUBDOMAINS=abc,xyz" -e "INTERVAL=10s" -d --name nc-ddns joshuamorris3/namecheap-ddns-update 94 | ``` 95 | 96 | ### Docker compose instructions 97 | 98 | Docker compose is a convienient way to start docker containers. The [docker-compose.yml](docker-compose.yml) provides an example, it uses a file called .env (see [.env.sample](.env.sample) for an example) to configure the namecheap-ddns-update process. 99 | 100 | To start the process using docker compose, simply run: 101 | ``` 102 | docker-compose up -d 103 | ``` 104 | 105 | ### Local docker instructions 106 | 107 | You can also run this from within a Docker container. 108 | 109 | Build your namecheap-ddns-update docker image 110 | ``` 111 | docker build -t namecheap-ddns-update -f Dockerfile . 112 | ``` 113 | 114 | Run the image you just built, passing in the environment variables to configure the script 115 | ``` 116 | docker run -e "NC_DDNS_PASS=123456" -e "DOMAIN=example.com" -e "SUBDOMAINS=abc,xyz" -e "INTERVAL=10s" -d --name nc-ddns namecheap-ddns-update 117 | ``` 118 | -------------------------------------------------------------------------------- /namecheap-ddns-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GLOBIGNORE="*" 3 | # for a domain you own hosted at namecheap, register one or more subdomains 4 | # to an IP address in namecheaps dynamicdns system 5 | 6 | if [ -r $(dirname "$0")/.env ]; then 7 | echo "Sourcing .env file at the same dir" 8 | source $(dirname "$0")/.env 9 | elif [ -r ~/.namecheap-ddns-update ]; then 10 | echo "Sourcing user ($USER) .namecheap-ddns-update file" 11 | source ~/.namecheap-ddns-update 12 | fi 13 | 14 | echo "" 15 | 16 | ############################################################################## 17 | # Update the domain subdomains 18 | ############################################################################## 19 | update() { 20 | local LOCAL_DOMAIN=$1 21 | local LOCAL_PWD=$2 22 | local LOCAL_SUBDOMAINS=$3 23 | echo "Updating domain: $LOCAL_DOMAIN, with subdomains: $LOCAL_SUBDOMAINS" 24 | # Create the static URL 25 | BASE_URL="https://dynamicdns.park-your-domain.com/update?domain=$LOCAL_DOMAIN&password=$LOCAL_PWD&ip=$IP" 26 | # Set the IP address for each raw_domain, subdomain combination 27 | # Only redefine IFS in the while loop 28 | while IFS=',' read -ra SUBDOMAIN_ARRAY; do 29 | for raw_subdomain in "${SUBDOMAIN_ARRAY[@]}"; do 30 | SUBDOMAIN="$(echo -e $raw_subdomain | tr -d '[[:space:]]')" 31 | echo "Registering subdomain: $SUBDOMAIN.$LOCAL_DOMAIN, to IP address: $IP" 32 | URL="$BASE_URL&host=$SUBDOMAIN" 33 | RESP="$(curl --silent --connect-timeout 10 $URL)" 34 | error_msg="$(echo -e $RESP | gawk '{ match($0, /(.*)<\/ErrCount>.+(.*)<\/Err1>/, arr); if (arr[1] > 0 ) print arr[2] }')" 35 | if [ "$error_msg" ]; then 36 | echo "ERROR : $error_msg" 1>&2; 37 | if [ "$EXIT_ON_ERROR" = true ]; then 38 | exit 1 39 | fi 40 | else 41 | new_ip="$(echo -e $RESP | gawk '{ match($0, /(.*)<\/IP>/, arr); if (arr[1] != "" ) print arr[1] }')" 42 | echo "$SUBDOMAIN.$LOCAL_DOMAIN IP address updated to: $new_ip" 43 | fi 44 | done 45 | done <<< "$LOCAL_SUBDOMAINS" 46 | echo "" 47 | } 48 | 49 | # Usage info 50 | show_help() { 51 | cat << EOF 52 | 53 | Usage: ${0##*/} [-h] [-e] [-d DOMAIN] [-s SUBDOMAINS] [-m MULTI_DOMAINS] [-i IP] [-t INTERVAL] 54 | 55 | Update the IP address of one or more domains subdomains that you own at 56 | namecheap.com. This can only update an existing A record, it cannot create 57 | a new A record. Use namecheap's advanced DNS settings for your domain to 58 | create A records. 59 | 60 | For details on how this works see: 61 | https://www.namecheap.com/support/knowledgebase/article.aspx/29/11/how-do-i-use-a-browser-to-dynamically-update-the-hosts-ip 62 | 63 | The args d, s, m, i and t have corresponding ENV options. The Dynamic DNS 64 | Password has to be set for each domain with the NC_DDNS_PASS environment 65 | variable. If there is one domain being updated, then the format of the 66 | NC_DNS_PASS value is the domain password. If you want to update multiple 67 | domains, then the value is a comma separated list of domain/password pairs. 68 | Example: NC_DNS_PASS=example.com=1a2s3d4f5g,example2.com=5g6h7j8k9l 69 | 70 | You could also create an environment file in the same directory as the script, 71 | called .env, or in directory of the user running this script, called 72 | .namecheap-ddns-update. The .env file is sourced first if found, if it does 73 | not exist, then .namecheap-ddns-update sourced if found. 74 | 75 | -h display this help and exit 76 | -e exit if any call to update a subdomains IP address fails 77 | -d DOMAIN the domain that has one or more SUBDOMAINS (A records) 78 | to update. DOMAIN/SUBDOMAINS can be used at the same 79 | time as MULTI_DOMAINS to update multiple domains 80 | -s SUBDOMAINS comma separated list of subdomains (A records) of DOMAIN 81 | to update. DOMAIN/SUBDOMAINS can be used at the same 82 | time as MULTI_DOMAINS to update multiple domains 83 | -m MULTI_DOMAINS other domains to combine with subdomains (A records). It 84 | can be specified multiple times on the command line, 85 | at the same time specified as the environment variable 86 | MULTI_DOMAINS. The format for the command line argument 87 | is: -m example.com=abc,xyz -m example2.com=def,ghi. The 88 | format for the environment variable is: 89 | MULTI_DOMAINS=example.com=abc,xyz:example2.com=def,ghi 90 | Each domain/subdomains pair is separated by a colon (:) 91 | -i IP IP address to set the subdomain(s) to. If blank namecheap 92 | will use the callers public IP address. 93 | -t INTERVAL set up a interval at which to run this. Uses bash sleep 94 | format e.g. NUMBER[SUFFIX] where SUFFIX can be, s for 95 | seconds (default), m for minutes, h for hours, d for days 96 | 97 | EOF 98 | } 99 | 100 | EXIT_ON_ERROR=false 101 | 102 | OPTIND=1 # Reset is necessary if getopts was used previously in the script. It is a good idea to make this local in a function. 103 | while getopts "hd:s:m:i:t:e" opt; do 104 | case "$opt" in 105 | h) 106 | show_help 107 | exit 0 108 | ;; 109 | d) DOMAIN=$OPTARG 110 | ;; 111 | s) SUBDOMAINS=$OPTARG 112 | ;; 113 | m) MULTI_DOMAINS_ARR+=("$OPTARG") 114 | ;; 115 | i) IP=$OPTARG 116 | ;; 117 | t) INTERVAL=$OPTARG 118 | ;; 119 | e) EXIT_ON_ERROR=true 120 | ;; 121 | *) 122 | show_help >&2 123 | exit 1 124 | ;; 125 | esac 126 | done 127 | shift "$((OPTIND-1))" # Shift off the options and optional --. 128 | 129 | ############################################################################## 130 | # Process the environment variable and script arguments 131 | ############################################################################## 132 | declare -A DOMAIN_PWDS 133 | declare -A DOMAIN_SUBDOMAINS 134 | 135 | ############################################################################## 136 | # Check and set the password 137 | # For a single domain, the NC_DDNS_PASS is a single value. When setting 138 | # multiple domains, the NC_DDNC_PASS is a comma separated list of key/value 139 | # pairs of the form 'domain=pass' e.g. example.com=123456789 140 | ############################################################################## 141 | : "${NC_DDNS_PASS:?Need to set the Dynamic DNS Password}" 142 | if [[ $NC_DDNS_PASS == *"="* ]]; then 143 | DOMAIN_PWDS_ARR=(${NC_DDNS_PASS//[=,]/ }) 144 | for (( i=0; i<${#DOMAIN_PWDS_ARR[@]}; i+=2 )); do 145 | DOMAIN_PWDS[${DOMAIN_PWDS_ARR[$i]}]=${DOMAIN_PWDS_ARR[$i+1]} 146 | done 147 | else 148 | DOMAIN_PWDS[$DOMAIN]=$NC_DDNS_PASS 149 | fi 150 | 151 | ############################################################################## 152 | # Check DOMAIN and SUBDOMAINS env or argument, and put the values into the 153 | # DOMAIN_SUBDOMAINS associative array. 154 | # These can only be set once, either as environment variables or via arguments 155 | # to this script 156 | ############################################################################## 157 | if [[ ! -z "$DOMAIN" ]]; then 158 | DOMAIN_SUBDOMAINS[$DOMAIN]=$SUBDOMAINS 159 | fi 160 | 161 | # Check if the MULTI_DOMAINS environment variable was set 162 | if [[ ! -z "$MULTI_DOMAINS" ]]; then 163 | DOMAIN_SUBDOMAINS_ARR=(${MULTI_DOMAINS//[=:]/ }) 164 | for (( i=0; i<${#DOMAIN_SUBDOMAINS_ARR[@]}; i+=2 )); do 165 | DOMAIN_SUBDOMAINS[${DOMAIN_SUBDOMAINS_ARR[$i]}]=${DOMAIN_SUBDOMAINS_ARR[$i+1]} 166 | done 167 | fi 168 | 169 | ############################################################################## 170 | # Check MULTI_DOMAINS_ARR arguments, and put the values into the 171 | # DOMAIN_SUBDOMAINS associative array. 172 | # The argument can be specified multiple times, each one is put into the 173 | # MULTI_DOMAINS_ARR array. Each array entry is of the form example.com=abc,zyx 174 | ############################################################################## 175 | if [[ ! -z "$MULTI_DOMAINS_ARR" ]]; then 176 | for multi_domain in "${MULTI_DOMAINS_ARR[@]}"; do 177 | DOMAIN_SUBDOMAINS_ARR=(${multi_domain//[=]/ }) 178 | for (( i=0; i<${#DOMAIN_SUBDOMAINS_ARR[@]}; i+=2 )); do 179 | DOMAIN_SUBDOMAINS[${DOMAIN_SUBDOMAINS_ARR[$i]}]=${DOMAIN_SUBDOMAINS_ARR[$i+1]} 180 | done 181 | done 182 | fi 183 | 184 | # Run the update, either one time or repeat on an interval 185 | if [ "$INTERVAL" ]; then 186 | # Run in a loop every inteval 187 | while [ : ]; do 188 | for domain in "${!DOMAIN_PWDS[@]}"; do 189 | update $domain ${DOMAIN_PWDS[$domain]} ${DOMAIN_SUBDOMAINS[$domain]} 190 | done 191 | sleep $INTERVAL 192 | done 193 | else 194 | # Run once and exit 195 | for domain in "${!DOMAIN_PWDS[@]}"; do 196 | update $domain ${DOMAIN_PWDS[$domain]} ${DOMAIN_SUBDOMAINS[$domain]} 197 | done 198 | fi 199 | --------------------------------------------------------------------------------