vdns 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a4d9ac01b82fdb26798ae44dee31fd629763537e
4
+ data.tar.gz: 7c34306f0902d28a7bc1ee610484728afa988ab1
5
+ SHA512:
6
+ metadata.gz: 2fa2824b695a7823273c8383802bc894e4f042296d9629f2e4fc2820eb507de1d7a656b612591376c88d6b8329625f4e4699042a69b33a422125f24e49203d9a
7
+ data.tar.gz: 3953cd201544813ce872a646c51de439c9983e9e7fb9cf1e26e09f582e4998888895bf58b7fa4feef593d57c292a28c83e53ef12cd47cff651bbdfce8a761ad5
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.gem
@@ -0,0 +1,39 @@
1
+ Dynamic DNS for developers and teams
2
+ ====================================
3
+
4
+ * Work locally with virtual hosts (online and offline)
5
+ * Test on devices in the same network
6
+ * Share with teammates in the same network
7
+ * Share with anybody over the web (with port forwarding)
8
+
9
+ This tool will create/ update ``user_name.domain A`` and ``*.user_name.domain A`` DNS records.
10
+
11
+
12
+ ```bash
13
+ vdns host # Update Nameserver with local loopback IP (host-only access)
14
+ vdns lan # Update Nameserver with local area IP (local network access)
15
+ vdns wan # Update Nameserver with wide area IP (Internet access)
16
+ vdns off # Delete Nameserver records
17
+
18
+
19
+ # specials (@todo):
20
+
21
+ vdns host --offline # Start a local Nameserver server and work offline
22
+ vdns wan --forward-port 80 # Set up Port forwarding in your router via UPnP
23
+ ```
24
+
25
+
26
+ ## Supported Nameservers
27
+
28
+ * [AWS Route 53](https://github.com/mattes/vdns/tree/master/lib/vdns/provider/aws-route53)
29
+
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ gem install vdns
35
+
36
+ # run vdns once and update config file ...
37
+ vdns
38
+ vi ~/.vdns/conf.yml
39
+ ```
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # when developing and calling ./vdns directly, uncomment the following line.
4
+ # $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
5
+
6
+ require "yaml"
7
+ require "optparse"
8
+ require "socket"
9
+ require 'open-uri'
10
+ require "vdns"
11
+
12
+ config = VDNS::Config.new("~/.vdns/conf.yml")
13
+ %w(provider user domain).each do |var|
14
+ if config.blank?(var)
15
+ puts "Error: Missing '#{var}' setting! Please define in #{config.file_name}."
16
+ exit 1
17
+ end
18
+ end
19
+
20
+
21
+ ip = nil
22
+ args = {}
23
+ args["offline"] = false
24
+ args["skip-check"] = false
25
+ args["ip"] = nil
26
+ args["port"] = nil
27
+ args["help"] = false
28
+ args["provider"] = "aws-route53"
29
+
30
+ opts = OptionParser.new do |o|
31
+ o.banner = "Update Nameserver with IP."
32
+ o.separator ""
33
+ o.separator "vdns host --offline, --skip-check"
34
+ o.separator "vdns lan --skip-check"
35
+ o.separator "vdns wan --forward-port, --skip-check"
36
+ o.separator "vdns custom --ip, --forward-port, --skip-check"
37
+ o.separator "vdns off"
38
+ o.separator ""
39
+ o.separator "Options:"
40
+ o.on("-o", "--offline", "Work in offline mode") { args["offline"] = true }
41
+ o.on("-p", "--forward-port [port]", "Set up Port forwarding via UPnP") {|var| args["port"] = var }
42
+ o.on("-i", "--ip [address]", "Use custom IP address") {|var| args["ip"] = var }
43
+ o.on("-s", "--skip-check", "Skip DNS check after update") { args["skip-check"] = true }
44
+ o.on("-h", "--help", "Show this help") { args["help"] = true }
45
+ end
46
+ opts.parse!
47
+
48
+ if args["help"]
49
+ puts opts
50
+ exit
51
+ end
52
+
53
+ # update helper method
54
+ def _update(config, provider_name, ip, verify=true)
55
+ provider = VDNS::ProviderFactory::get(provider_name, config)
56
+
57
+ puts "Updating: #{config.get("user")}.#{config.get("domain")} -> #{ip} (#{provider_name})"
58
+ ["*.#{config.get("user")}.#{config.get("domain")}",
59
+ "#{config.get("user")}.#{config.get("domain")}"].each do |domain|
60
+ provider.save(domain, ip)
61
+ end
62
+
63
+ if verify
64
+ check_ns = %w(8.8.8.8 8.8.4.4)
65
+ ns = VDNS::NSLookup.new(check_ns)
66
+
67
+ printf "Waiting for updates to become active ..."
68
+
69
+ def _sleep(seconds)
70
+ while seconds > 0
71
+ printf "." if seconds % 5 == 0
72
+ seconds -= 1
73
+ sleep 1
74
+ end
75
+ end
76
+
77
+ _sleep 65
78
+ updated = false
79
+ until ns.updated?("#{config.get("user")}.#{config.get("domain")}", ip)
80
+ _sleep 30
81
+ end
82
+
83
+ printf "\nUpdates should be active at nameservers #{check_ns.join(", ")}.\n"
84
+ end
85
+ end
86
+
87
+ def _port_forwarding(port)
88
+ unless port.nil?
89
+ puts "Warning: Port forwarding not supported, yet."
90
+ end
91
+ end
92
+
93
+
94
+ case ARGV[0]
95
+ when "host"
96
+ ip = "127.0.0.1"
97
+ if args["offline"]
98
+ puts "Error: Offline mode is not supported, yet."
99
+ exit 1
100
+ else
101
+ _update(config, args["provider"], ip, !args["skip-check"])
102
+ end
103
+
104
+
105
+ when "lan"
106
+ ips = Socket.ip_address_list
107
+ ips.reject! {|ip| not ip.ipv4_private? }
108
+ ips.map! {|ip| ip.ip_address}
109
+ ip = ips.first
110
+ _update(config, args["provider"], ip, !args["skip-check"])
111
+
112
+
113
+ when "wan"
114
+ ip = URI.parse('http://echoip.com').read.strip
115
+ _port_forwarding(args["port"])
116
+ _update(config, args["provider"], ip, !args["skip-check"])
117
+
118
+
119
+ when "custom"
120
+ if args["ip"].nil?
121
+ puts "Error: Missing --ip option."
122
+ exit 1
123
+ end
124
+ ip = args["ip"]
125
+ _port_forwarding(args["port"])
126
+ _update(config, args["provider"], ip, !args["skip-check"])
127
+
128
+ when "off"
129
+ provider = VDNS::ProviderFactory::get(args["provider"], config)
130
+ ["*.#{config.get("user")}.#{config.get("domain")}",
131
+ "#{config.get("user")}.#{config.get("domain")}"].each do |domain|
132
+ provider.delete(domain)
133
+ end
134
+
135
+ else
136
+ puts opts
137
+ exit
138
+ end
139
+
140
+
141
+
142
+
@@ -0,0 +1,3 @@
1
+ require "vdns/config"
2
+ require "vdns/provider"
3
+ require "vdns/nslookup"
@@ -0,0 +1,64 @@
1
+ module VDNS
2
+
3
+ class Config
4
+ @file = nil
5
+ @config = {}
6
+
7
+ def initialize(file)
8
+ @file = File.expand_path(file)
9
+
10
+ unless File.exist?(@file)
11
+ dirname = File.dirname(@file)
12
+ Dir.mkdir(dirname) unless Dir.exist?(dirname)
13
+ File.open(@file, "w") do |f|
14
+ f.puts %Q(
15
+ user: #{`whoami`}
16
+ domain:
17
+ provider: aws-route53
18
+
19
+ aws_r53_access_key:
20
+ aws_r53_secret_access_key:
21
+ aws_r53_hosted_zone_id:
22
+ )
23
+ end
24
+ end
25
+
26
+ read_file
27
+ end
28
+
29
+ def get(name)
30
+ if include?(name)
31
+ @config[name]
32
+ else
33
+ raise ArgumentError, "Undefined config variable #{name}"
34
+ end
35
+ end
36
+
37
+ def set(name, var)
38
+ @config[name] = var
39
+ end
40
+
41
+ def include?(name)
42
+ @config.include? name
43
+ end
44
+
45
+ def blank?(name)
46
+ @config[name].nil? || @config[name].empty?
47
+ end
48
+
49
+ def read_file
50
+ @config = YAML.load_file(@file)
51
+ end
52
+
53
+ def save_file
54
+ File.open(@file, 'w') do |f|
55
+ f.puts YAML.dump(@config)
56
+ end
57
+ end
58
+
59
+ def file_name
60
+ @file
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,25 @@
1
+ require "resolv"
2
+
3
+ module VDNS
4
+ class NSLookup
5
+
6
+ def initialize(nameservers)
7
+ raise ArgumentError, "Specify nameserver array" unless nameservers.is_a? Array
8
+ @nameservers = nameservers
9
+ end
10
+
11
+ def updated?(domain, to_ip)
12
+ updated = true
13
+ @nameservers.each do |ns|
14
+ resolv = Resolv::DNS.new(:nameserver => ns, :ndots => 1)
15
+ resolv.timeouts = 10
16
+ if resolv.getaddress(domain).to_s != to_ip
17
+ updated = false
18
+ break
19
+ end
20
+ end
21
+ return updated
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ module VDNS
2
+
3
+ class ProviderFactory
4
+ def self.get(provider, config)
5
+ require "vdns/provider/#{provider}/#{provider}"
6
+ return VDNS::Provider.new(config)
7
+ end
8
+ end
9
+
10
+
11
+ class ProviderBase
12
+ def warning(message)
13
+ puts "Warn: #{message}"
14
+ end
15
+
16
+ def error_and_exit(message)
17
+ puts "Error: #{message}"
18
+ exit 1
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,20 @@
1
+ AWS Route 53
2
+ ============
3
+
4
+ Amazon Web Services Route 53 Provider
5
+
6
+ http://docs.aws.amazon.com/AWSRubySDK/latest/frames.html
7
+
8
+ Required config variables:
9
+ * ``domain``
10
+ * ``aws_r53_access_key``
11
+ * ``aws_r53_secret_access_key``
12
+ * ``aws_r53_hosted_zone_id`` (see AWS Route 53 Console)
13
+
14
+ ## Setup
15
+
16
+ 1. Create new hosted zone in AWS console.
17
+ 1. Add hosted zone id to config file (aws_r53_hosted_zone_id).
18
+ 1. Set ``TTL (Seconds)`` to 60 for the SOA record.
19
+ 1. Set ``refresh-time, retry-time, expire-time, minimum TTL`` for SOA record to ``60 60 1209600 60``
20
+
@@ -0,0 +1,71 @@
1
+ require "vdns/provider"
2
+ require "aws-sdk"
3
+
4
+ module VDNS
5
+ class Provider < VDNS::ProviderBase
6
+
7
+ def initialize(config)
8
+ %w(domain
9
+ aws_r53_access_key
10
+ aws_r53_secret_access_key
11
+ aws_r53_hosted_zone_id).each do |var|
12
+ error_and_exit("Missing '#{var}' setting! Please define in #{config.file_name}.") if config.blank?(var)
13
+ end
14
+
15
+ AWS.config(access_key_id: config.get("aws_r53_access_key"),
16
+ secret_access_key: config.get("aws_r53_secret_access_key"))
17
+
18
+ @hosted_zone = AWS::Route53::HostedZone.new(config.get("aws_r53_hosted_zone_id"))
19
+ @rrsets = @hosted_zone.rrsets
20
+
21
+ @domain = parse_domain(config.get("domain"))
22
+ end
23
+
24
+ def save(domain, ip)
25
+ domain = parse_domain(domain)
26
+ rrset = @rrsets[domain, "A"]
27
+ if rrset.exists?
28
+ rrset.resource_records = [{:value => ip}]
29
+ rrset.update
30
+ else
31
+ @rrsets.create(
32
+ domain,
33
+ "A",
34
+ :ttl => 60,
35
+ :resource_records => [{:value => ip}])
36
+ end
37
+ update_soa_serial_number
38
+ return true
39
+ end
40
+
41
+ def delete(domain)
42
+ domain = parse_domain(domain)
43
+ rrset = @rrsets[domain, "A"]
44
+ if rrset.exists?
45
+ rrset.delete
46
+ update_soa_serial_number
47
+ end
48
+ return true
49
+ end
50
+
51
+ private
52
+
53
+ def parse_domain(domain)
54
+ unless domain.end_with?(".")
55
+ domain = "#{domain}."
56
+ end
57
+ domain = domain.gsub(/\*/, "\\\\052") # * -> 052 ascii, aws bug?!
58
+ return domain
59
+ end
60
+
61
+ def update_soa_serial_number
62
+ soa = @rrsets[@domain, "SOA"]
63
+ soa_parts = soa.resource_records[0][:value].split(" ")
64
+ soa_parts[2] = Time.now.to_i
65
+ soa.resource_records = [{:value => soa_parts.join(" ")}]
66
+ soa.update
67
+ end
68
+
69
+ end
70
+ end
71
+
@@ -0,0 +1,3 @@
1
+ module VDNS
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vdns/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "vdns"
8
+ spec.version = VDNS::VERSION
9
+ spec.authors = ["mattes"]
10
+ spec.email = ["matthias.kadenbach@gmail.com"]
11
+ spec.description = "Dynamic DNS for developers and teams."
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://github.com/mattes/vdns"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ # spec.add_development_dependency "bundler", "~> 1.3"
22
+ # spec.add_development_dependency "rspec"
23
+
24
+ spec.add_runtime_dependency "aws-sdk", "~> 1.28.0"
25
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vdns
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - mattes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-27 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: 1.28.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.28.0
27
+ description: Dynamic DNS for developers and teams.
28
+ email:
29
+ - matthias.kadenbach@gmail.com
30
+ executables:
31
+ - vdns
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - README.md
37
+ - bin/vdns
38
+ - lib/vdns.rb
39
+ - lib/vdns/config.rb
40
+ - lib/vdns/nslookup.rb
41
+ - lib/vdns/provider.rb
42
+ - lib/vdns/provider/aws-route53/README.md
43
+ - lib/vdns/provider/aws-route53/aws-route53.rb
44
+ - lib/vdns/version.rb
45
+ - vdns.gemspec
46
+ homepage: https://github.com/mattes/vdns
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.0.3
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: Dynamic DNS for developers and teams.
70
+ test_files: []