From 6a53b307ba5feb2979f2d19dd2db078dae391a03 Mon Sep 17 00:00:00 2001 From: David Kebler Date: Sun, 11 Oct 2020 16:54:58 -0700 Subject: [PATCH] first functional version --- .eslintrc.js | 37 ++++++++++++ .gitignore | 4 ++ config/custom-environment-variables.yaml | 19 ++++++ config/default.yaml | 11 ++++ config/dev.yaml | 5 ++ config/example-local.yaml | 23 ++++++++ index.js | 75 ++++++++++++++++++++++++ package.json | 37 ++++++++++++ readme.md | 7 +++ systemd.service | 0 10 files changed, 218 insertions(+) create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 config/custom-environment-variables.yaml create mode 100644 config/default.yaml create mode 100644 config/dev.yaml create mode 100644 config/example-local.yaml create mode 100644 index.js create mode 100644 package.json create mode 100644 readme.md create mode 100644 systemd.service diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..49bac18 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,37 @@ +module.exports = { + "ecmaFeatures": { + "modules": true, + "spread" : true, + "restParams" : true + }, + // "plugins": [ + // "unicorn" + // ], + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + // "unicorn/no-array-instanceof": "error", + "no-console": 0, + "semi": ["error", "never"], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6a93bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/node_modules/ +/coverage/ +*.lock +config/local* diff --git a/config/custom-environment-variables.yaml b/config/custom-environment-variables.yaml new file mode 100644 index 0000000..d7bf3d6 --- /dev/null +++ b/config/custom-environment-variables.yaml @@ -0,0 +1,19 @@ +network: NETWORK_DESC +domain: NETWORK_DOMAIN +notify: + services: + pushsafer: + k: PS_API_KEY, + d: PS_DEVICE_ID + email: + credentials: + user: GMAIL_SMTP_USER + pass: GMAIL_SMTP_PW +aws: + # region: 'us-east-1' # explicitly set AWS region + # sslEnabled: true # override whether SSL is enabled + #maxRetries: 3 # override the number of retries for a request + # timeout: 15000 + accessKeyId: AWS_ACCESS_KEY_ID # can omit access key and secret key + secretAccessKey: AWS_ACCESS_SECRET_KEY # if relying on a profile or IAM + profile: AWS_PROFILE # name of profile from ~/.aws/credentials diff --git a/config/default.yaml b/config/default.yaml new file mode 100644 index 0000000..abe05dd --- /dev/null +++ b/config/default.yaml @@ -0,0 +1,11 @@ +notify: + services: + pushsafer: + module: "@uci-utils/notify-pushsafer-plugin" + email: + module: "@uci-utils/notify-email-plugin" + subject: 'A Public IP Address has changed' +cron: 10 # interval in minutes to check to see if public ip has changed, comment to run just once or set to falsey +lookup: + interval: 10 # seconds to keep trying lookup of changed value + timeout: 2 # minutes before giving up - failed set diff --git a/config/dev.yaml b/config/dev.yaml new file mode 100644 index 0000000..f2254ed --- /dev/null +++ b/config/dev.yaml @@ -0,0 +1,5 @@ +network: 'testing kebler network' +domain: 'test.kebler.net' +dev: true +announce: true +cron: 1 diff --git a/config/example-local.yaml b/config/example-local.yaml new file mode 100644 index 0000000..4e4c91a --- /dev/null +++ b/config/example-local.yaml @@ -0,0 +1,23 @@ +# example local.yaml file +# copy to local.yaml and add secrets, ignored by git +# aws: # comment out this key to get default profile + # region: 'us-east-1' # explicitly set AWS region + # sslEnabled: true # override whether SSL is enabled + # maxRetries: 3 # override the number of retries for a request + # timeout: 15000 + # accessKeyId: 'my iam key' # can omit access key and secret key + # secretAccessKey: 'my secret' # if relying on a profile or IAM + # profile: aprofile # name of profile from ~/.aws/credentials +# notify: +# services: +# pushsafer: +# k: akey # api key +# d: anid # network admin user or group id +# email: +# credentials: +# user: blah@gmail.com +# pass: apismtppwsuppliedbygoogle +# to: +# - jimshoe@gmail.com +# - imapsuedonym@gmail.com +# subject: 'The subject of all notifiction emails' diff --git a/index.js b/index.js new file mode 100644 index 0000000..b326a95 --- /dev/null +++ b/index.js @@ -0,0 +1,75 @@ +import opts from 'config' +import Notifier from '@uci-utils/notify' +import Route53 from '@uci-utils/route53' +import pIp from 'public-ip' +import { lookup as clookup } from 'dns' +import { promisify } from 'util' +const lookup = promisify(clookup) +import { CronJob } from 'cron' + +console.log(JSON.stringify(opts, null, 3)) +const rname = process.argv[2] || opts.domain +if (!rname) { + console.log('exiting, must supply subdomain record name to check') + process.exit() +} + +const interval = (opts.lookup || {}).interval * 1000 || 10000 +const timeout = (opts.lookup || {}).timeout * 1000 * 60 || 120000 + +let notify=()=>{} // noop +const route53 = new Route53(opts.aws ? opts.aws : {profile:'default'}) +; +(async () => { + if (opts.notify){ + const notifier = await Notifier.create(opts.notify) + notify = notifier.send.bind(notifier) + } + + if (opts.cron) { + console.log('setting up check cron job every', opts.cron,'minutes') + const cronstr = `0 */${opts.cron} * * * *` + const job = new CronJob(cronstr, checkIP) + console.log('cron job starting with:',cronstr) + job.start() + } else { + console.log('running check a single time') + checkIP + } + +})().catch(err => { + console.log('FATAL: IP Updater! \n',err) + process.exitCode = 1 + process.kill(process.pid, 'SIGINT') // this will restart via systemd unit +}) + + +async function checkIP () { + let curr = await pIp.v4() + if (opts.dev) { + let ip = curr.split('.') + ip[0] = ip[0] - Math.floor(Math.random() * 10) + curr = ip.join('.') + } + const saved = await route53.getZoneRecordValue(rname) + if (opts.announce) notify('checking for change in public ip') + if (curr !== saved) { + let msg=`For Network:${opts.network}, a new public ip has been detected for ${rname} from ${saved} to ${curr}` + console.log(msg) + notify(msg) + msg=JSON.stringify(await route53.updateZoneRecordValue(rname,curr),null,3) + console.log(msg) + notify(msg) + const check = setInterval(async ()=>{ + if (curr === (await lookup(rname)).address) { + msg=`Update Succeeded, lookup of ${rname} is now ${curr}` + clearInterval(check) + } else msg=`Update Pending, will check again in ${(opts.lookup || {}).interval || 10 } seconds` + console.log(msg) + notify(msg) + } + ,(opts.lookup || {}).interval * 1000 || 10000) + // const fail = setTimeout(,timeout) + } else console.log('Public IP has not changed, nothing to do', saved) + +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4e2e0c3 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "updatePublicIp", + "version": "0.1.2", + "description": "", + "main": "src/route53.js", + "scripts": { + "start": "node -r esm index.js", + "dev": "NODE_ENV=dev nodemon -r esm index.js" + }, + "author": "David Kebler", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/uCOMmandIt/.git" + }, + "keywords": [ + "node.js" + ], + "bugs": { + "url": "https://github.com/uCOMmandIt/uci-utils/issues" + }, + "homepage": "https://github.com/uCOMmandIt/uci-utils#readme", + "dependencies": { + "@uci-utils/notify": "^0.1.5", + "@uci-utils/notify-email-plugin": "^0.1.5", + "@uci-utils/notify-pushsafer-plugin": "^0.1.2", + "@uci-utils/route53": "^0.1.2", + "config": "^3.3.2", + "cron": "^1.8.2", + "esm": "^3.2.25", + "js-yaml": "^3.14.0", + "public-ip": "^4.0.2" + }, + "devDependencies": { + "nodemon": "^1.19.4" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..6d3ed45 --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +### Update DNS Record with Public Ip + +Need a static IP for your network but don't want to pay for one? Run this node script which includes a cron job and your network can regularly check for changes in its public IP and update a DNS record at a AWS Route53 zone. + +Get notified when the network's public IP changes and that the DNS record has been sucessfully updated + +Setting/options in config directory diff --git a/systemd.service b/systemd.service new file mode 100644 index 0000000..e69de29