dnsign 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ require 'json'
2
+ require 'timers'
3
+ require 'net/http'
4
+
5
+ require 'dnsign/error'
6
+ require 'dnsign/ip_resolver'
7
+ require 'dnsign/dns_service'
8
+
9
+ module Dnsign
10
+ class ResolveUpdateLoop
11
+
12
+ def initialize(domain, dns_service, ip_resolver, opts={})
13
+ @domain = domain
14
+ @ip_resolver = ip_resolver
15
+ @dns_service = dns_service
16
+ @timers = Timers::Group.new
17
+
18
+ @verbose = opts[:verbose].nil? ? true : opts[:verbose]
19
+ @interval = opts[:interval] || 300
20
+ end
21
+
22
+ def kickoff
23
+ if @interval > 0
24
+ @timer = @timers.every( @interval ) { update }
25
+ loop { @timers.wait }
26
+ end
27
+ end
28
+
29
+ def update
30
+ real_ip = resolve_real_ip
31
+ dns_ip = retrieve_dns_ip
32
+
33
+ if real_ip != dns_ip
34
+ update_dns_ip(real_ip)
35
+ log "UPDATE: Public #{real_ip} differs from the resolved #{dns_ip}"
36
+ true
37
+ else
38
+ log "SKIPPING: Public #{real_ip} matches with the resolved #{dns_ip}"
39
+ false
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def resolve_real_ip
46
+ @ip_resolver.resolve
47
+ rescue InvalidResponseFromIpResolver => e
48
+ log e
49
+ end
50
+
51
+ def retrieve_dns_ip
52
+ @dns_service.retrieve_ip @domain
53
+ rescue RecordDoesNotExists => e
54
+ log e
55
+ end
56
+
57
+ def update_dns_ip(real_ip)
58
+ @dns_service.update_ip @domain, real_ip
59
+ rescue DomainDoesNotExists => e
60
+ log e
61
+ end
62
+
63
+ def log(msg)
64
+ if @verbose == true
65
+ puts ["[#{Time.now.utc}]", msg].join ' '
66
+ end
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module Dnsign
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,28 @@
1
+ ### Monkey patch
2
+ #
3
+ # DropletKit doesn't permit updating of 'data' attribute of domain record,
4
+ # even if DigitalOcean API documention says it should.
5
+ #
6
+ # https://github.com/digitalocean/droplet_kit/blob/master/lib/droplet_kit/mappings/domain_record_mapping.rb#L12
7
+ # https://developers.digitalocean.com/documentation/v2/#update-a-domain-record
8
+ # https://github.com/digitalocean/droplet_kit/issues/44
9
+ #
10
+
11
+ module DropletKit
12
+ class DomainRecordMapping
13
+ include Kartograph::DSL
14
+
15
+ kartograph do
16
+ mapping DomainRecord
17
+ root_key plural: 'domain_records', singular: 'domain_record', scopes: [:read]
18
+
19
+ property :id, scopes: [:read]
20
+ property :type, scopes: [:read, :create]
21
+ property :name, scopes: [:read, :create, :update]
22
+ property :data, scopes: [:read, :create, :update] # :update added
23
+ property :priority, scopes: [:read, :create]
24
+ property :port, scopes: [:read, :create]
25
+ property :weight, scopes: [:read, :create]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ require 'dnsign/dns_services/digital_ocean'
2
+
3
+ RSpec.describe DnsServices::DigitalOcean do
4
+
5
+ before :all do
6
+ @service = DnsServices::DigitalOcean.new access_token: '...'
7
+ end
8
+
9
+ describe '#update_ip' do
10
+
11
+ it 'creates new domain record' do
12
+ VCR.use_cassette('digital_ocean_create_domain_record') do
13
+ ip = @service.update_ip 'foo.floatingpoint.io', '127.0.0.1'
14
+ expect(ip).to eq('127.0.0.1')
15
+ end
16
+ end
17
+
18
+ it 'updates existing domain record' do
19
+ VCR.use_cassette('digital_ocean_update_domain_record') do
20
+ ip = @service.update_ip 'foo.floatingpoint.io', '127.0.1.1'
21
+ expect(ip).to eq('127.0.1.1')
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#retrieve_ip' do
27
+
28
+ it 'fetches existing domain record and returns its IP' do
29
+ VCR.use_cassette('digital_ocean_read_domain_record') do
30
+ ip = @service.retrieve_ip 'foo.floatingpoint.io'
31
+ expect(ip).to eq('127.0.0.1')
32
+ end
33
+ end
34
+
35
+ it 'returns nil for nonexisting record name' do
36
+ VCR.use_cassette('digital_ocean_read_domain_record') do
37
+ ip = @service.retrieve_ip 'bar.floatingpoint.io'
38
+ expect(ip).to be_nil
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ require 'dnsign/dns_services/linode'
2
+
3
+ RSpec.describe DnsServices::Linode do
4
+
5
+ before :all do
6
+ @service = DnsServices::Linode.new access_token: '...'
7
+ end
8
+
9
+ describe '#update_ip' do
10
+
11
+ it 'creates new domain record' do
12
+ VCR.use_cassette('linode_create_domain_record') do
13
+ ip = @service.update_ip 'foo.floatingpoint.io', '127.0.0.1'
14
+ expect(ip).to eq('127.0.0.1')
15
+ end
16
+ end
17
+
18
+ it 'updates existing domain record' do
19
+ VCR.use_cassette('linode_update_domain_record') do
20
+ ip = @service.update_ip 'foo.floatingpoint.io', '127.0.1.1'
21
+ expect(ip).to eq('127.0.1.1')
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#retrieve_ip' do
27
+
28
+ it 'fetches existing domain record and returns its IP' do
29
+ VCR.use_cassette('linode_read_domain_record') do
30
+ ip = @service.retrieve_ip 'foo.floatingpoint.io'
31
+ expect(ip).to eq('127.0.0.1')
32
+ end
33
+ end
34
+
35
+ it 'returns nil for nonexisting record name' do
36
+ VCR.use_cassette('linode_read_domain_record') do
37
+ ip = @service.retrieve_ip 'bar.floatingpoint.io'
38
+ expect(ip).to be_nil
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,129 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.digitalocean.com/v2/domains/floatingpoint.io/records
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ Authorization:
13
+ - Bearer ...
14
+ User-Agent:
15
+ - Faraday v0.9.1
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ Accept:
19
+ - "*/*"
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Server:
26
+ - cloudflare-nginx
27
+ Date:
28
+ - Mon, 13 Apr 2015 19:36:52 GMT
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Transfer-Encoding:
32
+ - chunked
33
+ Connection:
34
+ - keep-alive
35
+ Set-Cookie:
36
+ - __cfduid=d6dcb738dedb3817ef26bae5640926e731428953811; expires=Tue, 12-Apr-16
37
+ 19:36:51 GMT; path=/; domain=.digitalocean.com; HttpOnly
38
+ Status:
39
+ - 200 OK
40
+ X-Frame-Options:
41
+ - SAMEORIGIN
42
+ X-Xss-Protection:
43
+ - 1; mode=block
44
+ X-Content-Type-Options:
45
+ - nosniff
46
+ Ratelimit-Limit:
47
+ - '5000'
48
+ Ratelimit-Remaining:
49
+ - '4994'
50
+ Ratelimit-Reset:
51
+ - '1428957146'
52
+ Cache-Control:
53
+ - max-age=0, private, must-revalidate
54
+ X-Request-Id:
55
+ - d9dfb763-61af-496c-a227-56b524c3ee73
56
+ X-Runtime:
57
+ - '0.135043'
58
+ Cf-Ray:
59
+ - 1d699f4b5efa02db-AMS
60
+ body:
61
+ encoding: UTF-8
62
+ string: '{"domain_records":[{"id":1851008,"type":"NS","name":"@","data":"ns1.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851009,"type":"NS","name":"@","data":"ns2.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851010,"type":"NS","name":"@","data":"ns3.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851925,"type":"A","name":"@","data":"178.62.198.200","priority":null,"port":null,"weight":null},{"id":1851934,"type":"A","name":"floatingpoint.io","data":"178.62.198.200","priority":null,"port":null,"weight":null},{"id":1851939,"type":"CNAME","name":"www","data":"@","priority":null,"port":null,"weight":null},{"id":1929343,"type":"MX","name":"@","data":"aspmx.l.google.com","priority":1,"port":null,"weight":null},{"id":1929344,"type":"MX","name":"@","data":"alt1.aspmx.l.google.com","priority":5,"port":null,"weight":null},{"id":1929345,"type":"MX","name":"@","data":"alt2.aspmx.l.google.com","priority":5,"port":null,"weight":null},{"id":1929346,"type":"MX","name":"@","data":"aspmx2.googlemail.com","priority":10,"port":null,"weight":null},{"id":1929347,"type":"MX","name":"@","data":"aspmx3.googlemail.com","priority":10,"port":null,"weight":null},{"id":3671318,"type":"CNAME","name":"jabber","data":"@","priority":null,"port":null,"weight":null},{"id":3671325,"type":"SRV","name":"_xmpp-client._tcp","data":"jabber","priority":0,"port":5222,"weight":5},{"id":3671326,"type":"SRV","name":"_xmpp-server._tcp","data":"jabber","priority":0,"port":5269,"weight":5},{"id":5529034,"type":"CNAME","name":"structuredmind","data":"@","priority":null,"port":null,"weight":null}],"links":{},"meta":{"total":15}}'
63
+ http_version:
64
+ recorded_at: Mon, 13 Apr 2015 19:36:51 GMT
65
+ - request:
66
+ method: post
67
+ uri: https://api.digitalocean.com/v2/domains/floatingpoint.io/records
68
+ body:
69
+ encoding: UTF-8
70
+ string: '{"type":"A","name":"foo","data":"127.0.0.1","priority":null,"port":null,"weight":null}'
71
+ headers:
72
+ Content-Type:
73
+ - application/json
74
+ Authorization:
75
+ - Bearer 740ed6dd3e97e498892b2c2d0fd74376dc259c2b4cf898b168fbaa650171b732
76
+ User-Agent:
77
+ - Faraday v0.9.1
78
+ Accept-Encoding:
79
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
80
+ Accept:
81
+ - "*/*"
82
+ response:
83
+ status:
84
+ code: 201
85
+ message: Created
86
+ headers:
87
+ Server:
88
+ - cloudflare-nginx
89
+ Date:
90
+ - Mon, 13 Apr 2015 19:36:54 GMT
91
+ Content-Type:
92
+ - application/json; charset=utf-8
93
+ Transfer-Encoding:
94
+ - chunked
95
+ Connection:
96
+ - keep-alive
97
+ Set-Cookie:
98
+ - __cfduid=d6aa26e5acbbfad1f7cd829526d828c9c1428953813; expires=Tue, 12-Apr-16
99
+ 19:36:53 GMT; path=/; domain=.digitalocean.com; HttpOnly
100
+ Status:
101
+ - 201 Created
102
+ X-Frame-Options:
103
+ - SAMEORIGIN
104
+ X-Xss-Protection:
105
+ - 1; mode=block
106
+ X-Content-Type-Options:
107
+ - nosniff
108
+ Ratelimit-Limit:
109
+ - '5000'
110
+ Ratelimit-Remaining:
111
+ - '4993'
112
+ Ratelimit-Reset:
113
+ - '1428957146'
114
+ Etag:
115
+ - '"89d865317d5ead6eb139000676801d45"'
116
+ Cache-Control:
117
+ - max-age=0, private, must-revalidate
118
+ X-Request-Id:
119
+ - 4f54b117-79cb-4b13-be2b-929cb24d9362
120
+ X-Runtime:
121
+ - '0.240406'
122
+ Cf-Ray:
123
+ - 1d699f5961e700dd-AMS
124
+ body:
125
+ encoding: UTF-8
126
+ string: '{"domain_record":{"id":6221827,"type":"A","name":"foo","data":"127.0.0.1","priority":null,"port":null,"weight":null}}'
127
+ http_version:
128
+ recorded_at: Mon, 13 Apr 2015 19:36:53 GMT
129
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,65 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.digitalocean.com/v2/domains/floatingpoint.io/records
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ Authorization:
13
+ - Bearer ...
14
+ User-Agent:
15
+ - Faraday v0.9.1
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ Accept:
19
+ - "*/*"
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Server:
26
+ - cloudflare-nginx
27
+ Date:
28
+ - Mon, 13 Apr 2015 19:37:52 GMT
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Transfer-Encoding:
32
+ - chunked
33
+ Connection:
34
+ - keep-alive
35
+ Set-Cookie:
36
+ - __cfduid=de5d21637edd51517b39f58b486a037bc1428953871; expires=Tue, 12-Apr-16
37
+ 19:37:51 GMT; path=/; domain=.digitalocean.com; HttpOnly
38
+ Status:
39
+ - 200 OK
40
+ X-Frame-Options:
41
+ - SAMEORIGIN
42
+ X-Xss-Protection:
43
+ - 1; mode=block
44
+ X-Content-Type-Options:
45
+ - nosniff
46
+ Ratelimit-Limit:
47
+ - '5000'
48
+ Ratelimit-Remaining:
49
+ - '4990'
50
+ Ratelimit-Reset:
51
+ - '1428957146'
52
+ Cache-Control:
53
+ - max-age=0, private, must-revalidate
54
+ X-Request-Id:
55
+ - 92b081c0-f198-461c-a23e-e4db62582a7e
56
+ X-Runtime:
57
+ - '0.153754'
58
+ Cf-Ray:
59
+ - 1d69a0c204ab0c17-AMS
60
+ body:
61
+ encoding: UTF-8
62
+ string: '{"domain_records":[{"id":1851008,"type":"NS","name":"@","data":"ns1.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851009,"type":"NS","name":"@","data":"ns2.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851010,"type":"NS","name":"@","data":"ns3.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851925,"type":"A","name":"@","data":"178.62.198.200","priority":null,"port":null,"weight":null},{"id":1851934,"type":"A","name":"floatingpoint.io","data":"178.62.198.200","priority":null,"port":null,"weight":null},{"id":1851939,"type":"CNAME","name":"www","data":"@","priority":null,"port":null,"weight":null},{"id":1929343,"type":"MX","name":"@","data":"aspmx.l.google.com","priority":1,"port":null,"weight":null},{"id":1929344,"type":"MX","name":"@","data":"alt1.aspmx.l.google.com","priority":5,"port":null,"weight":null},{"id":1929345,"type":"MX","name":"@","data":"alt2.aspmx.l.google.com","priority":5,"port":null,"weight":null},{"id":1929346,"type":"MX","name":"@","data":"aspmx2.googlemail.com","priority":10,"port":null,"weight":null},{"id":1929347,"type":"MX","name":"@","data":"aspmx3.googlemail.com","priority":10,"port":null,"weight":null},{"id":3671318,"type":"CNAME","name":"jabber","data":"@","priority":null,"port":null,"weight":null},{"id":3671325,"type":"SRV","name":"_xmpp-client._tcp","data":"jabber","priority":0,"port":5222,"weight":5},{"id":3671326,"type":"SRV","name":"_xmpp-server._tcp","data":"jabber","priority":0,"port":5269,"weight":5},{"id":5529034,"type":"CNAME","name":"structuredmind","data":"@","priority":null,"port":null,"weight":null},{"id":6221827,"type":"A","name":"foo","data":"127.0.0.1","priority":null,"port":null,"weight":null}],"links":{},"meta":{"total":16}}'
63
+ http_version:
64
+ recorded_at: Mon, 13 Apr 2015 19:37:52 GMT
65
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,129 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.digitalocean.com/v2/domains/floatingpoint.io/records
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ Authorization:
13
+ - Bearer ...
14
+ User-Agent:
15
+ - Faraday v0.9.12
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ Accept:
19
+ - "*/*"
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Server:
26
+ - cloudflare-nginx
27
+ Date:
28
+ - Thu, 16 Apr 2015 19:29:20 GMT
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Transfer-Encoding:
32
+ - chunked
33
+ Connection:
34
+ - keep-alive
35
+ Set-Cookie:
36
+ - __cfduid=df01ae98350a0aa7920da8d212da6e7311429212560; expires=Fri, 15-Apr-16
37
+ 19:29:20 GMT; path=/; domain=.digitalocean.com; HttpOnly
38
+ Status:
39
+ - 200 OK
40
+ X-Frame-Options:
41
+ - SAMEORIGIN
42
+ X-Xss-Protection:
43
+ - 1; mode=block
44
+ X-Content-Type-Options:
45
+ - nosniff
46
+ Ratelimit-Limit:
47
+ - '5000'
48
+ Ratelimit-Remaining:
49
+ - '4999'
50
+ Ratelimit-Reset:
51
+ - '1429216160'
52
+ Cache-Control:
53
+ - max-age=0, private, must-revalidate
54
+ X-Request-Id:
55
+ - ccf97b83-481c-4cd1-bac0-3bf5e8c89d10
56
+ X-Runtime:
57
+ - '0.137237'
58
+ Cf-Ray:
59
+ - 1d824c65923b0557-VIE
60
+ body:
61
+ encoding: UTF-8
62
+ string: '{"domain_records":[{"id":1851008,"type":"NS","name":"@","data":"ns1.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851009,"type":"NS","name":"@","data":"ns2.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851010,"type":"NS","name":"@","data":"ns3.digitalocean.com","priority":null,"port":null,"weight":null},{"id":1851925,"type":"A","name":"@","data":"178.62.198.200","priority":null,"port":null,"weight":null},{"id":1851934,"type":"A","name":"floatingpoint.io","data":"178.62.198.200","priority":null,"port":null,"weight":null},{"id":1851939,"type":"CNAME","name":"www","data":"@","priority":null,"port":null,"weight":null},{"id":1929343,"type":"MX","name":"@","data":"aspmx.l.google.com","priority":1,"port":null,"weight":null},{"id":1929344,"type":"MX","name":"@","data":"alt1.aspmx.l.google.com","priority":5,"port":null,"weight":null},{"id":1929345,"type":"MX","name":"@","data":"alt2.aspmx.l.google.com","priority":5,"port":null,"weight":null},{"id":1929346,"type":"MX","name":"@","data":"aspmx2.googlemail.com","priority":10,"port":null,"weight":null},{"id":1929347,"type":"MX","name":"@","data":"aspmx3.googlemail.com","priority":10,"port":null,"weight":null},{"id":3671318,"type":"CNAME","name":"jabber","data":"@","priority":null,"port":null,"weight":null},{"id":3671325,"type":"SRV","name":"_xmpp-client._tcp","data":"jabber","priority":0,"port":5222,"weight":5},{"id":3671326,"type":"SRV","name":"_xmpp-server._tcp","data":"jabber","priority":0,"port":5269,"weight":5},{"id":5529034,"type":"CNAME","name":"structuredmind","data":"@","priority":null,"port":null,"weight":null},{"id":6232286,"type":"A","name":"office","data":"93.136.62.55","priority":null,"port":null,"weight":null}],"links":{},"meta":{"total":16}}'
63
+ http_version:
64
+ recorded_at: Thu, 16 Apr 2015 19:29:20 GMT
65
+ - request:
66
+ method: post
67
+ uri: https://api.digitalocean.com/v2/domains/floatingpoint.io/records
68
+ body:
69
+ encoding: UTF-8
70
+ string: '{"type":"A","name":"foo","data":"127.0.1.1","priority":null,"port":null,"weight":null}'
71
+ headers:
72
+ Content-Type:
73
+ - application/json
74
+ Authorization:
75
+ - Bearer 740ed6dd3e97e498892b2c2d0fd74376dc259c2b4cf898b168fbaa650171b732
76
+ User-Agent:
77
+ - Faraday v0.9.1
78
+ Accept-Encoding:
79
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
80
+ Accept:
81
+ - "*/*"
82
+ response:
83
+ status:
84
+ code: 201
85
+ message: Created
86
+ headers:
87
+ Server:
88
+ - cloudflare-nginx
89
+ Date:
90
+ - Thu, 16 Apr 2015 19:29:22 GMT
91
+ Content-Type:
92
+ - application/json; charset=utf-8
93
+ Transfer-Encoding:
94
+ - chunked
95
+ Connection:
96
+ - keep-alive
97
+ Set-Cookie:
98
+ - __cfduid=d9a857770dc4aca53f30819ba4871437d1429212561; expires=Fri, 15-Apr-16
99
+ 19:29:21 GMT; path=/; domain=.digitalocean.com; HttpOnly
100
+ Status:
101
+ - 201 Created
102
+ X-Frame-Options:
103
+ - SAMEORIGIN
104
+ X-Xss-Protection:
105
+ - 1; mode=block
106
+ X-Content-Type-Options:
107
+ - nosniff
108
+ Ratelimit-Limit:
109
+ - '5000'
110
+ Ratelimit-Remaining:
111
+ - '4998'
112
+ Ratelimit-Reset:
113
+ - '1429216160'
114
+ Etag:
115
+ - '"154a5bdcde3e91c31f170b4680f057b6"'
116
+ Cache-Control:
117
+ - max-age=0, private, must-revalidate
118
+ X-Request-Id:
119
+ - 3bbcd5f5-d1e8-490f-b6e8-7d431a563aa3
120
+ X-Runtime:
121
+ - '0.269614'
122
+ Cf-Ray:
123
+ - 1d824c6b973f0a3c-VIE
124
+ body:
125
+ encoding: UTF-8
126
+ string: '{"domain_record":{"id":6261986,"type":"A","name":"foo","data":"127.0.1.1","priority":null,"port":null,"weight":null}}'
127
+ http_version:
128
+ recorded_at: Thu, 16 Apr 2015 19:29:22 GMT
129
+ recorded_with: VCR 2.9.3