do-dyndns 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +39 -0
- data/bin/console +14 -0
- data/bin/dyndns +27 -0
- data/bin/setup +8 -0
- data/config.example.yml +4 -0
- data/lib/dyndns.rb +5 -0
- data/lib/dyndns/updater.rb +85 -0
- data/lib/dyndns/version.rb +3 -0
- metadata +110 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6acae6a533a232c1b1a6669a654a720a888ec149ab2caee17c08db14e81e5a11
|
4
|
+
data.tar.gz: '09b944f01f9b9aefbadfabb50c23c71a31981cee7817eb9117153d95ead2bd74'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db766ce0f59398b1834bf0e7f199730371aef8d0d41c64f1f912c07831bf4ddd25a2a3bf594a9c4233cc4f4697293138c493efc91c69f8983d65a39314f789d2
|
7
|
+
data.tar.gz: dacd17e32bce6057f759d0a4655aa0dd59da5110dbc6e26df48e3512f1ce9a12a2824002ca4d28f3b9ed974f97d1ca0ec1d9d61735e96426e2286312c1265d4d
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# DO-Dyndns
|
2
|
+
|
3
|
+
Automatically update DNS records on DigitalOcean
|
4
|
+
|
5
|
+
Finds the wan IPv4 address of the server it's running on and
|
6
|
+
updates the corresponding DNS records on digital ocean.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
`$ gem install do-dyndns`
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
Configuration is located at:
|
13
|
+
`~/.config/dyndns.yml`
|
14
|
+
|
15
|
+
if no config file is found, do-dyndns will create one and open it with your `$EDITOR`
|
16
|
+
|
17
|
+
```yaml
|
18
|
+
:token: your_digital_ocean_token_here
|
19
|
+
:domains:
|
20
|
+
example-domain1.com:
|
21
|
+
- example-subdomain1
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
```
|
26
|
+
$ dyndns
|
27
|
+
I, [2019-03-26T14:39:20.643564 #11387] INFO -- : Started IP check
|
28
|
+
I, [2019-03-26T14:39:20.720905 #11387] INFO -- : Current WAN IP: **.**.**.**
|
29
|
+
I, [2019-03-26T14:39:21.977426 #11387] INFO -- : IPs Match for ***.***.***
|
30
|
+
```
|
31
|
+
|
32
|
+
## Automation
|
33
|
+
|
34
|
+
### Cron:
|
35
|
+
Check every 15 minutes:
|
36
|
+
|
37
|
+
```
|
38
|
+
*/15 * * * * dyndns
|
39
|
+
```
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "dyndns"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/dyndns
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path(File.join(__dir__, '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'logger'
|
8
|
+
require 'shellwords'
|
9
|
+
require 'dyndns'
|
10
|
+
|
11
|
+
LOG = Logger.new($stdout)
|
12
|
+
|
13
|
+
rpath = File.expand_path(File.dirname(__FILE__))
|
14
|
+
Dir.chdir rpath
|
15
|
+
|
16
|
+
config_path = File.expand_path "~/.config/dyndns.yml"
|
17
|
+
|
18
|
+
if File.exist? config_path
|
19
|
+
config = YAML.load_file config_path
|
20
|
+
Dyndns::Updater.new(**config, logger: LOG).update_ips
|
21
|
+
else
|
22
|
+
LOG.warn "No configuration exists @ #{config_path}: Creating file."
|
23
|
+
FileUtils.mkdir_p File.dirname(config_path)
|
24
|
+
FileUtils.cp("../config.example.yml", config_path)
|
25
|
+
editor = ENV['EDITOR'] || 'nano'
|
26
|
+
system "#{editor} #{Shellwords.shellescape(config_path)}"
|
27
|
+
end
|
data/bin/setup
ADDED
data/config.example.yml
ADDED
data/lib/dyndns.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module Dyndns
|
2
|
+
class Updater
|
3
|
+
require 'droplet_kit'
|
4
|
+
|
5
|
+
def initialize(token:, domains:, logger: Logger.new($stdout))
|
6
|
+
@logger = logger
|
7
|
+
@domains = domains
|
8
|
+
@api = DropletKit::Client.new(access_token: token)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Get the domains from DO's API and selecto only ones specified in the config
|
12
|
+
def domains
|
13
|
+
@api.domains
|
14
|
+
.all
|
15
|
+
.select{ |d| @domains.keys.include? d.name }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the config specified sub domains for a domain
|
19
|
+
# - param {DropletKit::Domain} domain - The domain object
|
20
|
+
def sub_domains_for(domain)
|
21
|
+
@api.domain_records
|
22
|
+
.all(for_domain: domain.name)
|
23
|
+
.select{ |r| r.type == "A" && @domains[domain.name].include?(r.name) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check if a record ip differs from your current ip
|
27
|
+
# - param {DropletKit::DomainRecord} record - A domain record
|
28
|
+
def ip_changed?(record)
|
29
|
+
raise "Cannot determine WAN IPv4" unless _ip = wan_ipv4
|
30
|
+
record.data != _ip
|
31
|
+
end
|
32
|
+
|
33
|
+
# Calls update on all configured domain's subdomains
|
34
|
+
def update_ips
|
35
|
+
@logger.info "Started IP check"
|
36
|
+
@logger.info "Current WAN IP: #{wan_ipv4}"
|
37
|
+
|
38
|
+
domains.each do |domain|
|
39
|
+
sub_domains_for(domain).each do |record|
|
40
|
+
if ip_changed?(record)
|
41
|
+
@logger.info "IPs Differ for #{record.name}.#{domain.name}"
|
42
|
+
update(domain, record)
|
43
|
+
else
|
44
|
+
@logger.info "IPs Match for #{record.name}.#{domain.name}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update the associated domain with the current WAN IPv4 address
|
51
|
+
# - param {DropletKit::Domain} domain - The domain object
|
52
|
+
# - param {DropletKit::DomainRecord} record - A domain record
|
53
|
+
def update(domain, record)
|
54
|
+
@logger.info "Updating record #{record.name}.#{domain.name} from #{record.data} to #{wan_ipv4}"
|
55
|
+
record.data = wan_ipv4
|
56
|
+
@api.domain_records.update(record, for_domain: domain.name, id: record.id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def wan_ipv4
|
60
|
+
resolve([
|
61
|
+
"dig -4 @resolver1.opendns.com ANY myip.opendns.com +short",
|
62
|
+
"dig -4 @ns1-1.akamaitech.net ANY whoami.akamai.net +short",
|
63
|
+
"dig -4 @ns1.google.com ANY o-o.myaddr.l.google.com +short"
|
64
|
+
])
|
65
|
+
end
|
66
|
+
|
67
|
+
def wan_ipv6
|
68
|
+
resolve([
|
69
|
+
"dig -6 @resolver1.opendns.com ANY myip.opendns.com +short",
|
70
|
+
"dig -6 @ns1.google.com ANY o-o.myaddr.l.google.com +short"
|
71
|
+
])
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def resolve(commands)
|
77
|
+
_ip = nil
|
78
|
+
commands.each do |service|
|
79
|
+
_ip = `#{service}`.chomp.gsub(/[^a-z0-9\:\.]/i, '')
|
80
|
+
_ip = _ip and break unless _ip.empty?
|
81
|
+
end
|
82
|
+
_ip.empty? ? nil : _ip
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: do-dyndns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Clink
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: droplet_kit
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: |
|
70
|
+
Finds the wan IPv4 address of the server it's running on and
|
71
|
+
updates the corresponding DNS records on digital ocean.
|
72
|
+
email:
|
73
|
+
- alexclink@gmail.com
|
74
|
+
executables:
|
75
|
+
- dyndns
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- README.md
|
80
|
+
- bin/console
|
81
|
+
- bin/dyndns
|
82
|
+
- bin/setup
|
83
|
+
- config.example.yml
|
84
|
+
- lib/dyndns.rb
|
85
|
+
- lib/dyndns/updater.rb
|
86
|
+
- lib/dyndns/version.rb
|
87
|
+
homepage: http://alexclink.com/gems/dyndns
|
88
|
+
licenses:
|
89
|
+
- UNLICENSED
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubygems_version: 3.0.3
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: Automatically update DNS records on DigitalOcean
|
110
|
+
test_files: []
|