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 +7 -0
- data/.gitignore +10 -0
- data/Gemfile +3 -0
- data/Makefile +9 -0
- data/README.md +80 -0
- data/bin/dynamic-dns +10 -0
- data/dynamic_dns.gemspec +17 -0
- data/lib/dynamic_dns/daemon.rb +83 -0
- data/lib/dynamic_dns/dns_updater.rb +74 -0
- data/lib/dynamic_dns.rb +2 -0
- metadata +74 -0
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
data/Gemfile
ADDED
data/Makefile
ADDED
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
|
data/dynamic_dns.gemspec
ADDED
@@ -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
|
data/lib/dynamic_dns.rb
ADDED
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: []
|