do-dyndns 0.2.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
+ 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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ :token: your_digital_ocean_token_here
2
+ :domains:
3
+ example-domain1.com:
4
+ - example-subdomain1
data/lib/dyndns.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "dyndns/version"
2
+ require "dyndns/updater"
3
+
4
+ module Dyndns
5
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Dyndns
2
+ VERSION = "0.2.0"
3
+ 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: []