redzone 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +11 -0
- data/Gemfile +17 -0
- data/LICENSE.md +10 -0
- data/README.md +84 -0
- data/Rakefile +32 -0
- data/bin/redzone +7 -0
- data/lib/redzone/arpa.rb +66 -0
- data/lib/redzone/cli.rb +33 -0
- data/lib/redzone/environment.rb +20 -0
- data/lib/redzone/lifetime.rb +55 -0
- data/lib/redzone/machine.rb +70 -0
- data/lib/redzone/mail_exchange.rb +28 -0
- data/lib/redzone/name_server.rb +24 -0
- data/lib/redzone/record.rb +37 -0
- data/lib/redzone/soa.rb +64 -0
- data/lib/redzone/version.rb +6 -0
- data/lib/redzone/zone.rb +279 -0
- data/lib/redzone/zone_config.rb +28 -0
- data/lib/redzone/zonefile_writer.rb +30 -0
- data/lib/redzone.rb +2 -0
- data/redzone.gemspec +32 -0
- data/redzone.rb +279 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/unit/arpa_spec.rb +40 -0
- data/spec/unit/record_spec.rb +60 -0
- data/zones.yml +74 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZGIzOTRjZmY0MmE1YmNmYTA0YjFlODAxYzY5NzgxNDg0YmI5ODc3MA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YWY5OGI0YzMyOWNjMGNjOWY4ZThkOGJkMmJjMzBmNzYzYzRjNWQyMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTI0Y2ZhZjhmMTg3Y2VkODc3MDBhMDY0N2VlZmRjODhiMWJkODgxOTU3MmZk
|
10
|
+
NzBkZTYwNzk0ODVlYzBlNzYxNGViN2EzZjQwOTlhZTI2NTgzNmY2MzczM2My
|
11
|
+
ZDg4NjFmYTM5NzE0YmQ4MDRiZjI5NzdjOWM1ZWM4N2E1YTgwNWE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MTYzMTc3YjRkNjA2OTA3YjVhY2Q4ZDRjOTk2ZDkyYzJmZGFhNzY2ZTkzY2Qx
|
14
|
+
NTVjMzk5NTU5ODAxODQ1N2Y3ZjVmZTg0N2VlMGI5OWI2MmUzODM3NmVlNzcy
|
15
|
+
MDQzOGY4MTAzZDRmZTE0YmU1NGNlODYzNjlmYTViOTE2ZTYxZDk=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :development, :test do
|
6
|
+
gem 'rspec'
|
7
|
+
gem 'simplecov'
|
8
|
+
gem 'cucumber'
|
9
|
+
gem 'mocha', :require => 'mocha/api'
|
10
|
+
gem 'ci_reporter'
|
11
|
+
gem 'aruba'
|
12
|
+
end
|
13
|
+
|
14
|
+
group :development do
|
15
|
+
gem 'yard'
|
16
|
+
gem 'redcarpet', '~> 2.3.0'
|
17
|
+
end
|
data/LICENSE.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# @markup markdown
|
2
|
+
# @title MIT License
|
3
|
+
|
4
|
+
Copyright (C) 2014 Justen Walker
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
7
|
+
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
9
|
+
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# RedZone - Generate BIND zone files from simple YAML syntax
|
2
|
+
|
3
|
+
Introduction
|
4
|
+
------------
|
5
|
+
|
6
|
+
RedZone is a command-line utility for generating BIND DNS zone files from a YAML file. The tools follows the
|
7
|
+
UNIX philosophy and therefore does not attempt to do anything besides this.
|
8
|
+
|
9
|
+
RedZone was inspired by Daniel P. Berrange's [NoZone](http://search.cpan.org/~danberr/NoZone-1.0/lib/NoZone.pm) i
|
10
|
+
script which does something very similar in Perl.
|
11
|
+
The notable differences are:
|
12
|
+
- Added support for reverse-dns generation
|
13
|
+
- Removed zone inheritance, opting instead to just
|
14
|
+
use a common section from which all zones inherit details.
|
15
|
+
- No generation of named.conf.
|
16
|
+
|
17
|
+
Disclaimer
|
18
|
+
----------
|
19
|
+
|
20
|
+
This gem was just released (pre 1.0) and probably doesn't have everything (or anything) you want yet.
|
21
|
+
|
22
|
+
During pre 1.0, things may change that break backwards compatibility between releases. Most likely these breaking
|
23
|
+
changes would be related to the YAML file syntax.
|
24
|
+
|
25
|
+
### Current TODO List
|
26
|
+
|
27
|
+
- More test coverage for existing features
|
28
|
+
|
29
|
+
Installation
|
30
|
+
------------
|
31
|
+
|
32
|
+
TODO: Publish to RubyGems.org
|
33
|
+
|
34
|
+
Usage
|
35
|
+
-----
|
36
|
+
|
37
|
+
```
|
38
|
+
RedZone commands:
|
39
|
+
redzone generate DIR # Generates a bind database files into the given directory.
|
40
|
+
redzone help [COMMAND] # Describe available commands or one specific command
|
41
|
+
redzone version # Shows the current version of redzone
|
42
|
+
|
43
|
+
Options:
|
44
|
+
[--zones=ZONES] # RedZone zone file. (Default: /etc/redzone/zones.yml)
|
45
|
+
```
|
46
|
+
|
47
|
+
### Generating zone databases
|
48
|
+
|
49
|
+
- Create a zones.yml file (See [zones.yml.example]) in `/etc/redzone`
|
50
|
+
- Execute `redzone generate /var/named`
|
51
|
+
- Each zone and reverse record database should be present in /var/named
|
52
|
+
|
53
|
+
For example, if your zone.yml file is like the zones.yml.example file
|
54
|
+
then the resulting db files are:
|
55
|
+
- `0.0.0.0.0.0.0.0.0.0.c.f.ip6.arpa.db`
|
56
|
+
- `1.168.192.in-addr.arpa.db`
|
57
|
+
- `32.12.in-addr.arpa.db`
|
58
|
+
- `9.8.7.6.4.3.2.1.1.0.0.2.ip6.arpa.db`
|
59
|
+
- `qa.redzone.com.db`
|
60
|
+
- `redzone.com.db`
|
61
|
+
|
62
|
+
How to Contribute
|
63
|
+
-----------------
|
64
|
+
|
65
|
+
### Running the tests
|
66
|
+
|
67
|
+
$ bundle
|
68
|
+
$ bundle exec rake spec
|
69
|
+
|
70
|
+
### Installing locally
|
71
|
+
|
72
|
+
$ bundle
|
73
|
+
$ [bundle exec] rake install
|
74
|
+
|
75
|
+
### Reporting Issues
|
76
|
+
|
77
|
+
Please include a reproducible test case.
|
78
|
+
|
79
|
+
License
|
80
|
+
-------
|
81
|
+
|
82
|
+
Copyright (c) 2013 Justen Walker.
|
83
|
+
|
84
|
+
Released under the terms of the MIT License. For further information, please see the file [LICENSE.md].
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require "yard"
|
4
|
+
|
5
|
+
yard_files = ['lib/**/*.rb','-','LICENSE.md']
|
6
|
+
yard_opts = [
|
7
|
+
'--markup', 'markdown',
|
8
|
+
'--markup-provider', 'redcarpet',
|
9
|
+
'--readme', 'README.md',
|
10
|
+
'--no-private',
|
11
|
+
'--exclude', 'lib/*/cli.rb'
|
12
|
+
]
|
13
|
+
|
14
|
+
YARD::Rake::YardocTask.new do |t|
|
15
|
+
t.files = yard_files
|
16
|
+
t.options = yard_opts
|
17
|
+
end
|
18
|
+
task :yard_server => [:yard] do
|
19
|
+
system "yard server --reload"
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
require 'ci/reporter/rake/rspec'
|
23
|
+
rescue LoadError
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
27
|
+
t.pattern = "spec/unit/**/*_spec.rb"
|
28
|
+
end
|
29
|
+
|
30
|
+
task :default do
|
31
|
+
sh %{rake -T}
|
32
|
+
end
|
data/bin/redzone
ADDED
data/lib/redzone/arpa.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module RedZone
|
4
|
+
# Arpa definition
|
5
|
+
class Arpa
|
6
|
+
# Reverse DNS name
|
7
|
+
# @return [String] dns name
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
# Network
|
11
|
+
# @return [IPAddr]
|
12
|
+
attr_reader :network
|
13
|
+
|
14
|
+
# Get the list of PTR records
|
15
|
+
# @return [Array<Record>] PTR records
|
16
|
+
attr_reader :records
|
17
|
+
|
18
|
+
# Constructs a new MailExchange entry
|
19
|
+
# @param [Hash<String, SOA>] opt
|
20
|
+
# @option opt [String] :name Arpa DNS name (Required)
|
21
|
+
# @option opt [String] :network IP address with network mask (Required)
|
22
|
+
# @option opt [SOA,String] :soa SOA record (Required)
|
23
|
+
def initialize(opt)
|
24
|
+
raise ArgumentError, ':name is required' unless opt.has_key?(:name)
|
25
|
+
raise ArgumentError, ':network is required' unless opt.has_key?(:network)
|
26
|
+
raise ArgumentError, ':soa is required' unless opt.has_key?(:soa)
|
27
|
+
@name = opt[:name]
|
28
|
+
@network = IPAddr.new(opt[:network])
|
29
|
+
@soa = opt[:soa]
|
30
|
+
@records = []
|
31
|
+
end
|
32
|
+
|
33
|
+
# Writes the Arpa to the given IO stream
|
34
|
+
# @param [IO] io IO Stream
|
35
|
+
def write(io)
|
36
|
+
io << @soa
|
37
|
+
@records.each do |r|
|
38
|
+
io << r
|
39
|
+
end
|
40
|
+
io << "\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Adds a machine to the arpa network for reverse-address lookup
|
44
|
+
# only if the machine is in this network.
|
45
|
+
# @param [Machine] machine
|
46
|
+
# @param [String] domain name
|
47
|
+
def add(machine,domain)
|
48
|
+
fqdn = "#{machine.name}.#{domain}."
|
49
|
+
substr = ".#{@name}"
|
50
|
+
if @network.ipv4? and machine.ipv4? and @network.include?(machine.ipv4)
|
51
|
+
ip = machine.ipv4.reverse
|
52
|
+
ip.slice!(substr)
|
53
|
+
records << Record.new(:name => ip, :type => "PTR", :data => fqdn, :comment => "Machine #{machine.name}")
|
54
|
+
end
|
55
|
+
if @network.ipv6? and machine.ipv6? and @network.include?(machine.ipv6)
|
56
|
+
ip = machine.ipv6.ip6_arpa
|
57
|
+
ip.slice!(substr)
|
58
|
+
records << Record.new(:name => ip, :type => "PTR", :data => fqdn, :comment => "Machine #{machine.name}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_record(record)
|
63
|
+
records << record
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/redzone/cli.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
require 'redzone/zone_config'
|
4
|
+
require 'redzone/zonefile_writer'
|
5
|
+
require 'redzone/environment'
|
6
|
+
module RedZone
|
7
|
+
# RedZone Command-line actions
|
8
|
+
class Cli < Thor
|
9
|
+
package_name 'RedZone'
|
10
|
+
class_option :zones, :type => :string, :desc => <<-eos.strip
|
11
|
+
RedZone zone file. (Default: #{RedZone::Environment.default_zonefile})
|
12
|
+
eos
|
13
|
+
#class_option :config, :type => :string, :desc => <<-eos.strip
|
14
|
+
# RedZone configuration file. (Default: #{RedZone::Environment.default_configfile})
|
15
|
+
#eos
|
16
|
+
|
17
|
+
desc 'generate DIR', <<-eos.strip
|
18
|
+
Generates a bind database files into the given directory.
|
19
|
+
eos
|
20
|
+
# Generates a bind database files into the given directory.
|
21
|
+
def generate(dir)
|
22
|
+
c = ZoneConfig.new(options[:zones])
|
23
|
+
writer = ZonefileWriter.new(c)
|
24
|
+
writer.write_zones(Pathname.new(dir))
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'version', 'Shows the current version of redzone'
|
28
|
+
# Prints the current RedZone version to the console
|
29
|
+
def version
|
30
|
+
say "redzone v#{RedZone::VERSION}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RedZone
|
2
|
+
# RedZone environment properties
|
3
|
+
module Environment
|
4
|
+
# Get the default etc path
|
5
|
+
# @return [Pathname] default etc path
|
6
|
+
def self.default_etc
|
7
|
+
Pathname.new('/etc/redzone')
|
8
|
+
end
|
9
|
+
# Get the default location of the zone file
|
10
|
+
# @return [Pathname] default zones.yml path
|
11
|
+
def self.default_zonefile
|
12
|
+
self.default_etc.join('zones.yml')
|
13
|
+
end
|
14
|
+
# Get the default location of the config file
|
15
|
+
# @return [Pathname] default config.yml path
|
16
|
+
def self.default_configfile
|
17
|
+
self.default_etc.join('config.yml')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RedZone
|
2
|
+
# Simple time parser for TTL/SOA lifetimes
|
3
|
+
#
|
4
|
+
# The simple time format format is a number followed by a time unit.
|
5
|
+
#
|
6
|
+
# ie: `<Number> [Unit]`
|
7
|
+
#
|
8
|
+
# Where the `Unit` is one of
|
9
|
+
#
|
10
|
+
# - `M`: Minute(s)
|
11
|
+
# - `H`: Hour(s)
|
12
|
+
# - `D`: Day(s)
|
13
|
+
# - `W`: Week(s)
|
14
|
+
#
|
15
|
+
# If the units are missing, it is assumed to be in seconds
|
16
|
+
#
|
17
|
+
class Lifetime
|
18
|
+
# Constructs a lifetime object from a string
|
19
|
+
# @param [String] str time stirng
|
20
|
+
def initialize(str)
|
21
|
+
if str.upcase =~ /([0-9]+)\s*([HDWM]?)/
|
22
|
+
i = $1.to_i
|
23
|
+
pl = "s" if i > 1
|
24
|
+
pl ||= ""
|
25
|
+
time = case $2
|
26
|
+
when 'H' then [i * 3600,"#{i} Hour#{pl}"]
|
27
|
+
when 'D' then [i * 86400,"#{i} Day#{pl}"]
|
28
|
+
when 'M' then [i * 60,"#{i} Minute#{pl}"]
|
29
|
+
when 'W' then [i * 604800,"#{i} Week#{pl}"]
|
30
|
+
else [i,"#{i} Second#{pl}"]
|
31
|
+
end
|
32
|
+
@time = time.first
|
33
|
+
@str = time.last
|
34
|
+
end
|
35
|
+
end
|
36
|
+
# Returns the lifetime as seconds
|
37
|
+
# @return [Integer] seconds
|
38
|
+
def seconds
|
39
|
+
@time
|
40
|
+
end
|
41
|
+
# Returns the string representaton of the lifetime
|
42
|
+
#
|
43
|
+
# Examples:
|
44
|
+
#
|
45
|
+
# - 300 Seconds
|
46
|
+
# - 1 Minute
|
47
|
+
# - 2 Hours
|
48
|
+
# - 1 Day
|
49
|
+
# - 2 Weeks
|
50
|
+
# @return [String]
|
51
|
+
def to_s
|
52
|
+
@str
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module RedZone
|
4
|
+
# Machine entry
|
5
|
+
# @attr [String] name Relative domain name
|
6
|
+
# @attr [IPAddr] ipv4 IPV4 Address
|
7
|
+
# @attr [IPAddr] ipv6 IPV6 Address
|
8
|
+
class Machine
|
9
|
+
attr_reader :name,:ipv4,:ipv6
|
10
|
+
# Construct a new machine entry
|
11
|
+
# @param [String] name Relative domain name
|
12
|
+
# @param [Hash] config Machine configuration
|
13
|
+
# @option config [String] :ipv4 IPV4 Address
|
14
|
+
# @option config [String] :ipv6 IPV6 Address
|
15
|
+
def initialize(name,config)
|
16
|
+
@name = name
|
17
|
+
if config.is_a? Hash
|
18
|
+
@alias = nil
|
19
|
+
@ipv4 = IPAddr.new(config[:ipv4]) if config.has_key?(:ipv4)
|
20
|
+
@ipv6 = IPAddr.new(config[:ipv6]) if config.has_key?(:ipv6)
|
21
|
+
elsif config.is_a? Machine
|
22
|
+
@alias = config
|
23
|
+
@ipv4 = config.ipv4
|
24
|
+
@ipv6 = config.ipv6
|
25
|
+
end
|
26
|
+
end
|
27
|
+
# Returns true if this machine is an alias of another
|
28
|
+
# @return [Boolean] true if the machine is an alias
|
29
|
+
def alias?
|
30
|
+
not @alias.nil?
|
31
|
+
end
|
32
|
+
# Returns a new machine that is an alias of this machine.
|
33
|
+
# If this machine is already an alias, it delegates this call
|
34
|
+
# to the aliased machine rather than this one.
|
35
|
+
# @return [Machine]
|
36
|
+
def alias(name)
|
37
|
+
if @alias.nil?
|
38
|
+
Machine.new(name,self)
|
39
|
+
else
|
40
|
+
@alias.alias(name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
# Test if the machine has an ipv4 address
|
44
|
+
# @return [Boolean] if the machine has an ipv4 address
|
45
|
+
def ipv4?
|
46
|
+
not @ipv4.nil?
|
47
|
+
end
|
48
|
+
# Test if the machine has an ipv6 address
|
49
|
+
# @return [Boolean] if the machine has an ipv6 address
|
50
|
+
def ipv6?
|
51
|
+
not @ipv6.nil?
|
52
|
+
end
|
53
|
+
# Get the list of A/AAAA records
|
54
|
+
# @return [Array<Record>]
|
55
|
+
def records
|
56
|
+
r = []
|
57
|
+
comment = "Machine #{@alias.name}" if not @alias.nil?
|
58
|
+
if ipv4?
|
59
|
+
ipv4opt = {:name => @name, :data => @ipv4.to_s, :type => 'A', :comment => comment }
|
60
|
+
r << Record.new(ipv4opt)
|
61
|
+
end
|
62
|
+
if ipv6?
|
63
|
+
|
64
|
+
ipv6opt = {:name => @name, :data => @ipv6.to_s, :type => 'AAAA', :comment => comment }
|
65
|
+
r << Record.new(ipv6opt)
|
66
|
+
end
|
67
|
+
r
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RedZone
|
2
|
+
# A mail server record
|
3
|
+
class MailExchange
|
4
|
+
# MX Server name / alias
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
# Get the target machine hosting the mail exchange
|
8
|
+
attr_reader :machine
|
9
|
+
|
10
|
+
# MX Priority
|
11
|
+
attr_reader :priority
|
12
|
+
|
13
|
+
# Constructs a new MailExchange entry
|
14
|
+
# @param [String] name Server name / alias
|
15
|
+
# @param [Machine] machine Target machine
|
16
|
+
# @param [Integer] priority MX priority setting
|
17
|
+
def initialize(name,machine,priority)
|
18
|
+
@name = name
|
19
|
+
@machine = machine.alias(@name)
|
20
|
+
@priority = priority
|
21
|
+
end
|
22
|
+
# Get the list of MX records
|
23
|
+
# @return [Array<Record>]
|
24
|
+
def records
|
25
|
+
[Record.new(:name => "@", :type => "MX", :data => "#{@priority} #{@name}")]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RedZone
|
2
|
+
# NameServer record
|
3
|
+
class NameServer
|
4
|
+
# Name Server name / alias
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
# Get the target machine hosting the name server
|
8
|
+
attr_reader :machine
|
9
|
+
|
10
|
+
# Constructs a new NameServer
|
11
|
+
# @param [String] name Server name / alias
|
12
|
+
# @param [Machine] machine Target machine
|
13
|
+
def initialize(name,machine)
|
14
|
+
@name = name
|
15
|
+
@machine = machine.alias(@name)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the list of NS records
|
19
|
+
# @return [Array<Record>]
|
20
|
+
def records
|
21
|
+
[Record.new(:name => "@", :type => "NS", :data => "#{@name}")]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'redzone/lifetime'
|
2
|
+
module RedZone
|
3
|
+
# DNS Record
|
4
|
+
class Record
|
5
|
+
# Returns a new instance of a domain record
|
6
|
+
# @param [Hash] record
|
7
|
+
# @option record [String] :name The record name (Required)
|
8
|
+
# @option record [String] :class ('IN') The record class. (Optional)
|
9
|
+
# @option record [String] :ttl The ttl for the record (Optional)
|
10
|
+
# @option record [String] :type The type of record, eg: CNAME, A, AAAA. (Required)
|
11
|
+
# @option record [String] :data The record data (Required)
|
12
|
+
# @option record [String] :comment A comment for the record
|
13
|
+
def initialize(record)
|
14
|
+
raise ArgumentError, ':name is required' unless record.has_key?(:name)
|
15
|
+
raise ArgumentError, ':type is required' unless record.has_key?(:type)
|
16
|
+
raise ArgumentError, ':data is required' unless record.has_key?(:data)
|
17
|
+
@name = record[:name]
|
18
|
+
@class = record[:class] || 'IN'
|
19
|
+
@type = record[:type]
|
20
|
+
if record.has_key?(:ttl) and not record[:ttl].nil?
|
21
|
+
@ttl = Lifetime.new(record[:ttl]).seconds
|
22
|
+
end
|
23
|
+
@data = record[:data]
|
24
|
+
if @type == "TXT"
|
25
|
+
@data = '"%s"' % [@data.gsub(/^\s*"?|"?\s*$/,'').gsub(/\n/,'')]
|
26
|
+
end
|
27
|
+
if record.has_key?(:comment) and not record[:comment].nil?
|
28
|
+
@comment = " ; %s" % [record[:comment]]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# Returns the domain record as a string to be written in the zone file
|
32
|
+
# @return [String] record line
|
33
|
+
def to_s
|
34
|
+
"%-20s %-8s %s %-8s %-20s%s\n" % [@name, @ttl, @class, @type, @data, @comment || '']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/redzone/soa.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'redzone/lifetime'
|
2
|
+
module RedZone
|
3
|
+
# A DNS Start of Authority (SOA) record
|
4
|
+
#
|
5
|
+
# Example
|
6
|
+
# -------
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# $ORIGIN example.com.
|
10
|
+
# $TTL 3600 ; TTL = 1 Hour
|
11
|
+
# @ IN SOA ns1 hostmaster.example.com. (
|
12
|
+
# 20140104 ; sn = serial number
|
13
|
+
# 3600 ; ref = refresh = 1 Hour
|
14
|
+
# 600 ; rt = update retry = 10 Minutes
|
15
|
+
# 86400 ; ex = expiry = 1 Day
|
16
|
+
# )
|
17
|
+
#
|
18
|
+
class SOA
|
19
|
+
# Returns a new instance of a zone SOA record
|
20
|
+
# @param [Hash] opts
|
21
|
+
# @option opts [String] :domain Zone (domain) name
|
22
|
+
# @option opts [String] :ns Primary name server
|
23
|
+
# @option opts [String] :hostmaster Email address of the zone file maintainer ('@' replaced by '.')
|
24
|
+
# @option opts [String] :refresh Time between refreshes from slave servers
|
25
|
+
# @option opts [String] :retry Time between retrying failed zone transfers
|
26
|
+
# @option opts [String] :expire The maximum time that a secondary server will keep trying to complete a zone transfer.
|
27
|
+
# @option opts [String] :ttl The minimum time-to-live that applies to all resource records in the zone file
|
28
|
+
def initialize(opts)
|
29
|
+
@domain = opts[:domain]
|
30
|
+
@ns = opts[:ns]
|
31
|
+
@hostmaster = escape(opts[:hostmaster] || "hostmaster.#{@domain}")
|
32
|
+
@ttl = Lifetime.new(opts[:ttl] || '1H')
|
33
|
+
@refresh = Lifetime.new(opts[:refresh] || '1H')
|
34
|
+
@retry = Lifetime.new(opts[:retry] || '10M')
|
35
|
+
@expire = Lifetime.new(opts[:expire] || '1D')
|
36
|
+
end
|
37
|
+
# Returns the SOA record with the serial set to the current unix timestamp
|
38
|
+
# @return [String] SOA Record
|
39
|
+
def to_s
|
40
|
+
to_soa(Time.now.to_i)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the SOA record with the given serial number
|
44
|
+
# @param [Integer] serial Serial number (Usually YYYYMMDDnn)
|
45
|
+
# @return [String] SOA Record
|
46
|
+
def to_soa(serial)
|
47
|
+
io = StringIO.new
|
48
|
+
io << "$ORIGIN #{@domain}.\n"
|
49
|
+
io << "$TTL #{@ttl.seconds} ; TTL = #{@ttl}\n"
|
50
|
+
io << "@ IN SOA #{@ns} #{@hostmaster} (\n"
|
51
|
+
io << " %-10s ; sn = serial number\n" % [serial]
|
52
|
+
io << " %-10s ; ref = refresh = %s\n" % [@refresh.seconds,@refresh]
|
53
|
+
io << " %-10s ; rt = retry = %s\n" % [@retry.seconds,@retry]
|
54
|
+
io << " %-10s ; ex = expiry = %s\n" % [@expire.seconds,@expire]
|
55
|
+
io << " %-10s ; ttl = %s\n" % [@ttl.seconds,@ttl]
|
56
|
+
io << ")\n\n"
|
57
|
+
io.string
|
58
|
+
end
|
59
|
+
private
|
60
|
+
def escape(email)
|
61
|
+
email.gsub(/@/,'.')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|