dynamic_dns 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ccee403a3b6f04ab955a2712570ceb80643151e
4
+ data.tar.gz: 4e932eaa73d5ecf5fa375aa1d24da204f0f60df8
5
+ SHA512:
6
+ metadata.gz: 852ce7f2eb7c0e97d1d6511ca4737342ddcbc2b99624a06a79e06be5d770f392feac8ba1ebdb822a3715d6001b04b5b1e787d686209c509713cafd457994000a
7
+ data.tar.gz: c71c90c498e98e4f6a190e132e851cb302ff8acce8e502321b129541ef23082ebe370938d46644437b7a6c8a8745a2615ca66d8f51395e3b2c554329ab9d1113
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.tmp
2
+ *.swp
3
+ *.DS_Store
4
+ *.gem
5
+
6
+ Gemfile.lock
7
+
8
+ .bundle/
9
+
10
+ vendor/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Makefile ADDED
@@ -0,0 +1,9 @@
1
+ run:
2
+ bundle exec ruby -I lib bin/dynamic-dns
3
+
4
+ install:
5
+ bundle install --path vendor/bundle
6
+
7
+ dependencies:
8
+ @ echo '0. ruby (use your package manager)'
9
+ @ echo '1. bundle (gem install bundle)'
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # DynamicDNS
2
+
3
+ DynamicDNS is tool that queries your public IPv4 address periodically, and updates a specific `A` record in Amazon Route53 if this IP address changes.
4
+
5
+ This gem is useful as a command-line executable, and as a library.
6
+
7
+
8
+ ## Usage
9
+
10
+ The following usage examples will configure the daemon to run every 10 minutes, updating the hosted zone for `foo.com` in Route53 to have an `A` record that points `ssh.foo.com` to the computer's public IPv4 address. See the *Configuration* section below for more information on the environment variables used to configure the daemon.
11
+
12
+ Ensure you have installed this gem to use the command-line tool. You must have Ruby 2.4.0, or greater, installed.
13
+
14
+ ```bash
15
+ $ gem install dynamic_dns
16
+ ```
17
+
18
+
19
+ ### Command-line
20
+
21
+ ```bash
22
+ # use environment variables to configure the daemon
23
+ # exit with Ctrl+c
24
+ $ DYNAMIC_DNS_DOMAIN=foo.com DYNAMIC_DNS_SUBDOMAIN=ssh DYNAMIC_DNS_INTERVAL=600 dynamic-dns
25
+ ```
26
+
27
+
28
+ ### `systemd` Start-Up Script
29
+
30
+ If you are using `rvm` with `systemd`, ensure that `rvm` was installed using `sudo`, then install this gem.
31
+
32
+ ```ini
33
+ [Unit]
34
+ Description=DynamicDNS daemon
35
+
36
+ [Service]
37
+ Type=simple
38
+ Environment="GEM_HOME=/usr/local/rvm/gems/ruby-2.4.1"
39
+ Environment="GEM_PATH=/usr/local/rvm/gems/ruby-2.4.1:/usr/local/rvm/gems/ruby-2.4.1@global"
40
+ Environment="PATH=/usr/local/rvm/gems/ruby-2.4.1/bin:/usr/local/rvm/gems/ruby-2.4.1@global/bin:/usr/local/rvm/rubies/ruby-2.4.1/bin:$PATH"
41
+ Environment="AWS_ACCESS_KEY_ID=[REDACTED]"
42
+ Environment="AWS_SECRET_ACCESS_KEY=[REDACTED]"
43
+ Environment="DYNAMIC_DNS_DOMAIN=foo.com"
44
+ Environment="DYNAMIC_DNS_SUBDOMAIN=ssh"
45
+ Environment="DYNAMIC_DNS_INTERVAL=600"
46
+ ExecStart=/usr/local/rvm/gems/ruby-2.4.1/bin/dynamic-dns
47
+
48
+ [Install]
49
+ WantedBy=multi-user.target
50
+ ```
51
+
52
+
53
+ ### Ruby
54
+
55
+ ```ruby
56
+ require 'dynamic-dns'
57
+
58
+ daemon = DynamicDns::Daemon.new "foo.com", "ssh", 600
59
+ daemon.start # runs the daemon and blocks the thread
60
+ ```
61
+
62
+
63
+ ## Confirguation
64
+
65
+ It is possible to configure this daemon using environment variables.
66
+
67
+ | Variable | Required? | Default | Example | Description |
68
+ |:---|:---|:---|:---|:---|
69
+ | `DYNAMIC_DNS_DOMAIN` | Y | N/A | `foo.com` | This domain's hosted zone will be updated. It must already have an existing hosted zone in Route53 prior to running the daemon. |
70
+ | `DYNAMIC_DNS_SUBDOMAIN` | Y | N/A | `ssh` | The name of the A record that will point to the public IP address discovered by the daemon. |
71
+ | `DYNAMIC_DNS_INTERVAL` | N | `60` | `60` | The interval in seconds that the daemon should query for the public IPv4 address. |
72
+ | `AWS_ACCESS_KEY_ID` | Y | N/A | `abc123` | Your AWS access key ID. It must have permissions to manage Route53. This requirement is defined by the `aws-sdk` gem ([link](https://github.com/aws/aws-sdk-ruby)). An alternative method to providnig your AWS credentials to create an `~/.aws/credentials` file. |
73
+ | `AWS_SECRET_ACCESS_KEY` | Y | N/A | `abc123` | Your AWS secret access key. See above for notes on AWS credentials. |
74
+
75
+
76
+ ## Author
77
+
78
+ Dhruv Dang
79
+ [hi@dhruv.io](mailto:hi@dhruv.io)
80
+ [dhruv.io](https://dhruv.io)
data/bin/dynamic-dns ADDED
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'dynamic_dns'
4
+
5
+ DOMAIN = ENV['DYNAMIC_DNS_DOMAIN']
6
+ SUBDOMAIN = ENV['DYNAMIC_DNS_SUBDOMAIN']
7
+ INTERVAL = ENV['DYNAMIC_DNS_INTERVAL']
8
+
9
+ daemon = DynamicDns::Daemon.new DOMAIN, SUBDOMAIN, (INTERVAL and Integer INTERVAL)
10
+ daemon.start
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'dynamic_dns'
3
+ gem.version = '0.1.0'
4
+ gem.date = '2017-07-17'
5
+ gem.summary = 'Dynamic DNS'
6
+ gem.description = 'Daemon to update an Amazon Route53 A record with a computer\'s dynamic IPv4 address.'
7
+ gem.author = 'Dhruv Dang'
8
+ gem.email = 'hi@dhruv.io'
9
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename f }
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.require_paths = [ 'lib' ]
12
+ gem.license = 'MIT'
13
+ gem.homepage = 'https://github.com/dhruvio/rb_dynamic-dns'
14
+ gem.required_ruby_version = '>=2.4'
15
+
16
+ gem.add_runtime_dependency 'aws-sdk', '~> 2.10', '>= 2.10.9'
17
+ end
@@ -0,0 +1,83 @@
1
+ require 'net/http'
2
+ require 'aws-sdk'
3
+ require 'dynamic_dns/dns_updater'
4
+
5
+ module DynamicDns
6
+
7
+ class Daemon
8
+
9
+ # create URI to fetch IP address with
10
+ @@GET_IP_URI = URI('https://ipinfo.io/ip')
11
+
12
+ def initialize(domain, subdomain, interval)
13
+ log 'created instance'
14
+ # initialize ip address
15
+ @ip = 'NO_IP_SET'
16
+ # initialize the daemon IP interval if necessary
17
+ interval.is_a? Integer or interval = 60
18
+ @INTERVAL = interval
19
+ # set up the dns updater instance
20
+ @UPDATER = DynamicDns::DnsUpdater.new domain, subdomain
21
+ end
22
+
23
+ # accessor methods
24
+ attr_reader :ip
25
+ attr_reader :INTERVAL
26
+
27
+ # helper to fetch the IP address
28
+ # returns a string represents the ip address
29
+ def fetch_ip
30
+ log 'fetching ip'
31
+ Net::HTTP.get(@@GET_IP_URI)
32
+ end
33
+
34
+ def start
35
+ log "starting daemon, will check for new ip every #@INTERVAL seconds"
36
+ # set up the thread to loop
37
+ thread = Thread.new do
38
+ log 'set up thread'
39
+ loop do
40
+ log 'start thread loop iteration'
41
+ # get the ip address
42
+ # if it has changed, update DNS
43
+ new_ip = fetch_ip
44
+ if new_ip != @ip
45
+ on_new_ip(new_ip)
46
+ else
47
+ log "ip has not changed from #@ip"
48
+ end
49
+ # sleep the number of seconds in the interval
50
+ sleep @INTERVAL
51
+ end
52
+ end
53
+
54
+ # ensure thread exits on an exception
55
+ thread.abort_on_exception = true
56
+
57
+ # trap SIGINT to exit threads
58
+ trap 'SIGINT' do
59
+ log "received SIGINT, killing thread"
60
+ thread.kill
61
+ end
62
+
63
+ # keep main thread attached while background thread runs
64
+ thread.join
65
+ end
66
+
67
+ def on_new_ip(new_ip)
68
+ log "ip has changed from #@ip to #{new_ip}"
69
+ #persist new ip to instance
70
+ @ip = new_ip
71
+ #notify aws
72
+ log "update DNS with new IP #@ip"
73
+ # interval corresponds to ttl in seconds
74
+ @UPDATER.update_record_ip @ip, @INTERVAL
75
+ end
76
+
77
+ def log(msg)
78
+ puts "[dynamic-dns:daemon] #{msg}"
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,74 @@
1
+ require 'aws-sdk'
2
+ require 'json'
3
+
4
+ module DynamicDns
5
+
6
+ class DnsUpdater
7
+
8
+ def initialize(domain, subdomain = nil)
9
+ log 'created instance'
10
+ # error handling
11
+ domain.is_a? String or raise RuntimeError, "Invalid domain, must be a String: #{domain}"
12
+ subdomain and subdomain.is_a? String or raise RuntimeError, "Invalid subdomain, must be nil or a String: #{subdomain}"
13
+ # set up constants
14
+ @DOMAIN = domain
15
+ @NAME = create_name(@DOMAIN, subdomain)
16
+ @RECORD_TYPE = 'A'
17
+ @CLIENT = Aws::Route53::Client.new(region: 'us-east-1')
18
+ # fetch the available hosted zones
19
+ log('fetch hosted zones')
20
+ zones = @CLIENT.list_hosted_zones_by_name
21
+ zones = zones.hosted_zones
22
+ # search for the hosted zone matching the configured domain
23
+ log("match hosted zone for domain: #{@DOMAIN}")
24
+ @ZONE = zones.bsearch { |z| z.name.match?("^#{@DOMAIN}") }
25
+ @ZONE or raise RuntimeError, "No hosted zones match #{@DOMAIN}"
26
+ end
27
+
28
+ # accessor methods
29
+ attr_reader :DOMAIN
30
+ attr_reader :NAME
31
+
32
+ def create_name(domain, subdomain)
33
+ if subdomain
34
+ "#{subdomain}.#{domain}"
35
+ else
36
+ domain
37
+ end
38
+ end
39
+
40
+ def update_record_ip(ip, ttl = 60)
41
+ log "update #{@RECORD_TYPE} record for #{@NAME} to point to #{ip}"
42
+ resp = @CLIENT.change_resource_record_sets({
43
+ change_batch: {
44
+ changes: [
45
+ {
46
+ action: 'UPSERT',
47
+ resource_record_set: {
48
+ name: @NAME,
49
+ resource_records: [
50
+ {
51
+ value: ip
52
+ }
53
+ ],
54
+ type: @RECORD_TYPE,
55
+ ttl: ttl
56
+ }
57
+ }
58
+ ],
59
+ comment: 'Update from dynamic dns ruby script'
60
+ },
61
+ hosted_zone_id: @ZONE.id
62
+ })
63
+ resp = resp.change_info
64
+ log "update of id #{resp.id} is #{resp.status} at #{resp.submitted_at}"
65
+ end
66
+
67
+ # prefixed log function
68
+ def log(msg)
69
+ puts "[dynamic-dns:dns-updater] #{msg}"
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,2 @@
1
+ require 'dynamic_dns/daemon'
2
+ require 'dynamic_dns/dns_updater'
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dynamic_dns
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dhruv Dang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.10'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.10.9
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.10'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.10.9
33
+ description: Daemon to update an Amazon Route53 A record with a computer's dynamic
34
+ IPv4 address.
35
+ email: hi@dhruv.io
36
+ executables:
37
+ - dynamic-dns
38
+ extensions: []
39
+ extra_rdoc_files: []
40
+ files:
41
+ - ".gitignore"
42
+ - Gemfile
43
+ - Makefile
44
+ - README.md
45
+ - bin/dynamic-dns
46
+ - dynamic_dns.gemspec
47
+ - lib/dynamic_dns.rb
48
+ - lib/dynamic_dns/daemon.rb
49
+ - lib/dynamic_dns/dns_updater.rb
50
+ homepage: https://github.com/dhruvio/rb_dynamic-dns
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '2.4'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.6.11
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Dynamic DNS
74
+ test_files: []