ript 0.8.4

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.
Files changed (79) hide show
  1. data/.gitignore +6 -0
  2. data/.rbenv-version +1 -0
  3. data/AUTHORS.md +16 -0
  4. data/CHANGELOG.md +93 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +62 -0
  7. data/LICENCE +19 -0
  8. data/README.md +564 -0
  9. data/Rakefile +136 -0
  10. data/bin/rbenv-sudo +18 -0
  11. data/bin/ript +207 -0
  12. data/dist/init.d +48 -0
  13. data/examples/accept-multiple-from-and-to.rb +16 -0
  14. data/examples/accept-with-a-list-of-ports.rb +13 -0
  15. data/examples/accept-with-specific-port-and-interface.rb +14 -0
  16. data/examples/accept-without-specific-from.rb +11 -0
  17. data/examples/accept.rb +12 -0
  18. data/examples/basic.rb +4 -0
  19. data/examples/dash-in-partition-name.rb +2 -0
  20. data/examples/drop.rb +11 -0
  21. data/examples/duplicate-partition-names/foobar1.rb +2 -0
  22. data/examples/duplicate-partition-names/foobar2.rb +2 -0
  23. data/examples/errors-undefined-method-with-no-match.rb +12 -0
  24. data/examples/errors-undefined-method.rb +12 -0
  25. data/examples/forward-dnat-with-different-destination-port.rb +16 -0
  26. data/examples/forward-dnat-with-explicit-from-and-port-mappings.rb +11 -0
  27. data/examples/forward-dnat-with-explicit-from-and-ports.rb +11 -0
  28. data/examples/forward-dnat-with-explicit-from.rb +11 -0
  29. data/examples/forward-dnat-with-explicit-protocols.rb +15 -0
  30. data/examples/forward-dnat-with-multiple-froms.rb +13 -0
  31. data/examples/forward-dnat-with-multiple-ports.rb +10 -0
  32. data/examples/forward-dnat-with-multiple-sources.rb +15 -0
  33. data/examples/forward-dnat.rb +16 -0
  34. data/examples/forward-snat-with-explicit-from.rb +16 -0
  35. data/examples/forward-snat-with-multiple-sources.rb +13 -0
  36. data/examples/forward-snat.rb +9 -0
  37. data/examples/log-and-accept.rb +12 -0
  38. data/examples/log-and-drop.rb +11 -0
  39. data/examples/log-dnat.rb +10 -0
  40. data/examples/log-snat.rb +13 -0
  41. data/examples/log.rb +11 -0
  42. data/examples/missing-address-definition-in-destination.rb +15 -0
  43. data/examples/missing-address-definition-in-from.rb +15 -0
  44. data/examples/multiple-partitions-in-this-file.rb +14 -0
  45. data/examples/multiple-partitions/bar.rb +11 -0
  46. data/examples/multiple-partitions/foo.rb +17 -0
  47. data/examples/partition-name-exactly-20-characters.rb +2 -0
  48. data/examples/partition-name-longer-than-20-characters.rb +2 -0
  49. data/examples/postclean.rb +10 -0
  50. data/examples/preclean.rb +10 -0
  51. data/examples/raw-with-chain-deletion.rb +9 -0
  52. data/examples/raw-with-flush.rb +9 -0
  53. data/examples/raw.rb +50 -0
  54. data/examples/reject.rb +11 -0
  55. data/examples/space-in-partition-name.rb +2 -0
  56. data/features/cli.feature +115 -0
  57. data/features/dsl/errors.feature +107 -0
  58. data/features/dsl/filter.feature +187 -0
  59. data/features/dsl/logging.feature +114 -0
  60. data/features/dsl/nat.feature +271 -0
  61. data/features/dsl/raw.feature +28 -0
  62. data/features/setup.feature +58 -0
  63. data/features/step_definitions/cli_steps.rb +15 -0
  64. data/features/step_definitions/example_steps.rb +44 -0
  65. data/features/support/env.rb +25 -0
  66. data/lib/ript/bootstrap.rb +20 -0
  67. data/lib/ript/dsl.rb +14 -0
  68. data/lib/ript/dsl/primitives.rb +7 -0
  69. data/lib/ript/dsl/primitives/common.rb +78 -0
  70. data/lib/ript/dsl/primitives/filter.rb +145 -0
  71. data/lib/ript/dsl/primitives/nat.rb +206 -0
  72. data/lib/ript/dsl/primitives/raw.rb +45 -0
  73. data/lib/ript/exceptions.rb +2 -0
  74. data/lib/ript/partition.rb +162 -0
  75. data/lib/ript/patches.rb +10 -0
  76. data/lib/ript/rule.rb +70 -0
  77. data/lib/ript/version.rb +3 -0
  78. data/ript.gemspec +33 -0
  79. metadata +232 -0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #!/usr/bin/env ruby
4
+
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ require 'cucumber'
8
+ require 'cucumber/rake/task'
9
+ require 'colorize'
10
+ require 'pathname'
11
+ $: << Pathname.new(__FILE__).join('lib').expand_path.to_s
12
+ require 'ript/version'
13
+
14
+ Cucumber::Rake::Task.new(:features) do |t|
15
+ t.cucumber_opts = "features --format pretty"
16
+ end
17
+
18
+ desc "Build packages for various platforms"
19
+ #task :build => [ 'build:gem', 'build:deb' ]
20
+ task :build => [ :verify, 'build:gem', 'build:deb' ]
21
+
22
+ namespace :build do
23
+ desc "Build RubyGem"
24
+ task :gem do
25
+ build_output = `gem build ript.gemspec`
26
+ puts build_output
27
+
28
+ gem_filename = build_output[/File: (.*)/,1]
29
+ pkg_path = "pkg"
30
+ FileUtils.mkdir_p(pkg_path)
31
+ FileUtils.mv(gem_filename, pkg_path)
32
+
33
+ puts "Gem built at #{pkg_path}/#{gem_filename}".green
34
+ end
35
+
36
+ desc "Build a deb for Ubuntu"
37
+ task :deb => :gem do
38
+ gem_filename = "pkg/ript-#{Ript::VERSION}.gem"
39
+ deb_filename = "pkg/ript-#{Ript::VERSION}.deb"
40
+ system("rm -f #{deb_filename}")
41
+ build_output = `fpm -s gem -t deb -p #{deb_filename} #{gem_filename}`
42
+
43
+ require 'json'
44
+ json = build_output[/({.+})$/, 1]
45
+ data = JSON.parse(json)
46
+ if path = data["path"]
47
+ puts "Deb built at #{path}".green
48
+ end
49
+ end
50
+ end
51
+
52
+ namespace :verify do
53
+ desc "Verify the CHANGELOG is in order for a release"
54
+ task :changelog do
55
+ changelog_filename = "CHANGELOG.md"
56
+ version = Ript::VERSION
57
+ command = "grep '^# #{version}' #{changelog_filename} 2>&1 >/dev/null"
58
+
59
+ if not system(command)
60
+ puts "#{changelog_filename} doesn't have an entry for the version (#{version}) you are about to build.".red
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ desc "Verify there are no uncommitted files"
66
+ task :uncommitted do
67
+ uncommitted = `git ls-files -m`.split("\n")
68
+ if uncommitted.size > 0
69
+ puts "The following files are uncommitted:".red
70
+ uncommitted.each do |filename|
71
+ puts " - #{filename}".red
72
+ end
73
+ exit 1
74
+ end
75
+ end
76
+
77
+ desc "Verify no requires of RubyGems have snuck in"
78
+ task :no_rubygems do
79
+ requires = `grep rubygems lib/ bin/ -rn |grep require`.split("\n")
80
+ if requires.size > 0
81
+ puts "The following files use RubyGems:".red
82
+ requires.each do |filename|
83
+ puts " - #{filename}".red
84
+ end
85
+ exit 1
86
+ end
87
+ end
88
+
89
+ task :all => [ :changelog, :uncommitted, :no_rubygems ]
90
+ end
91
+
92
+ task :verify => 'verify:all'
93
+
94
+
95
+
96
+ desc "Clean out the state of iptables"
97
+ task :clean_slate do
98
+ # Clean filter
99
+ system("sudo iptables --flush --table filter")
100
+ system("sudo iptables --delete-chain --table filter")
101
+ system("sudo iptables --table filter --policy INPUT ACCEPT")
102
+ system("sudo iptables --table filter --policy FORWARD ACCEPT")
103
+ system("sudo iptables --table filter --policy OUTPUT ACCEPT")
104
+
105
+ # Clean NAT
106
+ system("sudo iptables --flush --table nat")
107
+ system("sudo iptables --delete-chain --table nat")
108
+ system("sudo iptables --table nat --policy PREROUTING ACCEPT")
109
+ system("sudo iptables --table nat --policy POSTROUTING ACCEPT")
110
+ system("sudo iptables --table nat --policy OUTPUT ACCEPT")
111
+
112
+ # Clean mangle
113
+ system("sudo iptables --flush --table mangle")
114
+ system("sudo iptables --delete-chain --table mangle")
115
+ system("sudo iptables --table mangle --policy PREROUTING ACCEPT")
116
+ system("sudo iptables --table mangle --policy POSTROUTING ACCEPT")
117
+ system("sudo iptables --table mangle --policy INPUT ACCEPT")
118
+ system("sudo iptables --table mangle --policy FORWARD ACCEPT")
119
+ system("sudo iptables --table mangle --policy OUTPUT ACCEPT")
120
+
121
+ # Verify
122
+ puts "### FILTER ###"
123
+ system("sudo iptables --list --table filter")
124
+ puts
125
+
126
+ puts "### NAT ###"
127
+ system("sudo iptables --list --table nat")
128
+ puts
129
+
130
+ puts "### MANGLE ###"
131
+ system("sudo iptables --list --table mangle")
132
+ puts
133
+ end
134
+
135
+
136
+
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+
3
+ if [ "$(whoami)" != "root" ]; then
4
+ echo "You must be root to run this!"
5
+ exit 1
6
+ fi
7
+
8
+ if [ -d "$HOME/.rbenv" ]; then
9
+ export PATH="$HOME/.rbenv/bin:$PATH"
10
+ fi
11
+ if [ -d "/opt/rbenv" ]; then
12
+ export PATH="/opt/rbenv/bin:$PATH"
13
+ export RBENV_ROOT="/opt/rbenv"
14
+ fi
15
+
16
+ eval "$(rbenv init -)"
17
+
18
+ $@
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+ $: << Pathname.new(__FILE__).parent.parent.join('lib').expand_path.to_s
5
+ # so rules/ can be loaded
6
+ $: << Pathname.new(__FILE__).parent.parent.expand_path.to_s
7
+ $: << Dir.pwd
8
+ require 'ript/dsl'
9
+
10
+ if RUBY_VERSION =~ /^1.8/ then
11
+ puts "Ript requires Ruby 1.9 to run. Exiting."
12
+ exit
13
+ end
14
+
15
+ if Process.uid != 0 then
16
+ puts "You must run this as root!"
17
+ exit 1
18
+ end
19
+
20
+ if not ARGV[0]
21
+ puts "Usage: #{$0} <rulefile|directory>"
22
+ exit! 1
23
+ end
24
+
25
+ def types
26
+ {
27
+ :a => 'filter',
28
+ :d => 'nat',
29
+ :s => 'nat',
30
+ }
31
+ end
32
+
33
+ def current_chain_names_by_partition
34
+ # Collect the full iptables output
35
+ output = {}
36
+ types.each_pair do |type, table|
37
+ output[type] = `iptables --table #{table} --list partition-#{type} --numeric 2>&1 | grep -v 'No chain/target/match by that name'`.split("\n")
38
+ end
39
+
40
+
41
+ blacklist = %w(PREROUTING POSTROUTING OUTPUT INPUT FORWARD Chain target before-a after-a partition-a partition-d partition-s)
42
+ chains = {}
43
+
44
+ types.keys.each do |type|
45
+ chains[type] = {}
46
+ output[type].each do |line|
47
+ chain_name = line.split(/ /).first
48
+ next if blacklist.include? chain_name
49
+ partition = chain_name.split(/-/).first
50
+ chains[type][partition] ||= []
51
+ chains[type][partition] << chain_name
52
+ end
53
+ end
54
+
55
+ # Add the chains that aren't referenced anywhere to the end
56
+ ['nat', 'filter'].each do |table|
57
+ unlisted = `iptables --table #{table} --list --numeric 2>&1 | grep 'Chain'`.split("\n")
58
+ unlisted = unlisted.map {|l| l.split(/ /)[1]} - blacklist
59
+ unlisted.each do |chain_name|
60
+ partition, type = chain_name.split(/-/)
61
+ type = type[0].to_sym
62
+ chains[type][partition] ||= []
63
+ unless chains[type][partition].include? chain_name
64
+ chains[type][partition] << chain_name
65
+ end
66
+ end
67
+ end
68
+ chains
69
+ end
70
+
71
+ if ARGV[0] == 'rules'
72
+ if ARGV[1] == "generate" or ARGV[1] == "diff" then
73
+ path = Pathname.new(ARGV[2])
74
+
75
+ case
76
+ when path.directory?
77
+ path = (path + "**/*.rb").to_s
78
+ files = Pathname.glob(path)
79
+ files.each do |file|
80
+ require "#{file}"
81
+ end
82
+ when path.exist?
83
+ begin
84
+ require "#{path}"
85
+ rescue LoadError
86
+ puts "The specified rule file '#{path}' does not exist"
87
+ exit 160
88
+ end
89
+ else
90
+ puts "The specified rule file or directory '#{path}' does not exist"
91
+ exit 160
92
+ end
93
+
94
+ if `iptables --list partition-a --numeric 2>&1 | grep Chain` !~ /^Chain/
95
+ require 'ript/bootstrap'
96
+ puts "# bootstrap"
97
+ puts Ript::Bootstrap.partition.to_iptables
98
+ end
99
+
100
+ if ARGV[1] == "generate"
101
+ @partitions.each do |partition|
102
+ puts "# #{partition.name}-#{partition.id}"
103
+ puts partition.to_iptables
104
+ end
105
+ end
106
+
107
+ if ARGV[1] == "diff"
108
+ @partitions.each do |partition|
109
+ # We assume here that if a partition has a partition-a chain it will have all the others
110
+ chain_name = "#{partition.name}-#{partition.id.split('-').first}".sub(/-/, '-a')
111
+ unless current_chain_names_by_partition[:a].has_key?(partition.name) && current_chain_names_by_partition[:a][partition.name].include?(chain_name)
112
+ puts "# #{partition.name}-#{partition.id}"
113
+ puts partition.to_iptables
114
+ end
115
+ end
116
+ end
117
+
118
+ exit
119
+ end
120
+
121
+ if ARGV[1] == "apply" then
122
+ output = `#{$0} rules diff #{ARGV[2..-1].join(' ')} 2>&1`
123
+ puts "#{output}"
124
+ system("bash -c '#{output}'")
125
+
126
+ exit
127
+ end
128
+
129
+ if ARGV[1] == 'save' then
130
+ system('/sbin/iptables-save')
131
+ exit
132
+ end
133
+
134
+ puts "Usage: #{$0} <rules> <generate|diff|apply|save> <rulefile|directory>"
135
+ exit! 1
136
+ end
137
+
138
+ if ARGV[0] == "clean" then
139
+ if ARGV[1] == "diff" then
140
+ path = Pathname.new(ARGV[2])
141
+
142
+ case
143
+ when path.directory?
144
+ path = (path + "**/*.rb").to_s
145
+ files = Pathname.glob(path)
146
+ files.each do |file|
147
+ require "#{file}"
148
+ end
149
+ when path.exist?
150
+ begin
151
+ require "#{path}"
152
+ rescue LoadError => e
153
+ puts e
154
+ puts "The specified rule file '#{path}' does not exist"
155
+ exit 160
156
+ end
157
+ else
158
+ puts "The specified rule file or directory '#{path}' does not exist"
159
+ exit 160
160
+ end
161
+
162
+ current_chain_names_by_partition.each_pair do |type, partitions|
163
+ partitions.each_pair do |partition, chains|
164
+ # If we are in file mode don't remove other partitions
165
+ next if File.file?(path) && ! @partitions.map(&:name).include?(partition)
166
+ partition_obj = @partitions.find {|c| c.name == partition }
167
+ unless partition_obj.nil?
168
+ chain_name = "#{partition_obj.name}-#{type}#{partition_obj.id.split('-').first}"
169
+ chains = chains - [chain_name]
170
+ end
171
+
172
+ chains.uniq.each do |chain|
173
+ table = types[type]
174
+
175
+ clean_command = `iptables-save --table #{table} 2>&1 | grep -- '-A partition-#{type}' | grep -- '-j #{chain}'`.split("\n")
176
+ clean_command.map! {|line| "iptables --table #{table} #{line}" }
177
+ clean_command.map! {|line| line.gsub(" -A", " --delete") }
178
+ clean_command.map! {|line| line.gsub(" -s", " --source") }
179
+ clean_command.map! {|line| line.gsub(" -d", " --destination") }
180
+ clean_command.map! {|line| line.gsub(" -j", " --jump") }
181
+ clean_command.map! {|line| line.strip }
182
+ puts clean_command
183
+
184
+ puts "iptables --table #{table} --flush #{chain}"
185
+ puts "iptables --table #{table} --delete-chain #{chain}"
186
+
187
+ end
188
+ end
189
+ end
190
+ exit
191
+ end
192
+
193
+ if ARGV[1] == "apply" then
194
+ output = `#{$0} clean diff #{ARGV[2..-1].join(' ')} 2>&1`
195
+ puts "#{output}"
196
+ system("bash -c '#{output}'")
197
+
198
+ exit
199
+ end
200
+
201
+ puts "Usage: #{$0} <clean> <diff|apply> <rulefile|directory>"
202
+ exit! 1
203
+ end
204
+
205
+
206
+ puts "Usage: #{$0} <rules|clean|save> <generate|diff|apply> <rulefile|directory>"
207
+ exit! 1
@@ -0,0 +1,48 @@
1
+ #!/bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: ript
4
+ # Required-Start:
5
+ # Required-Stop:
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1 6
8
+ # Short-Description: start and stop ript firewall
9
+ # Description: Start, stop and save ript firewall
10
+ ### END INIT INFO
11
+
12
+ # Author: John Ferlito <johnf@bulletproof.net>
13
+
14
+ PATH=/sbin:/bin
15
+ DESC="Restore ript firewall"
16
+ NAME=ript
17
+ IPTABLES_RESTORE=/sbin/iptables-restore
18
+ IPTABLES_STATE=/var/lib/ript/iptables.state
19
+ SCRIPTNAME=/etc/init.d/$NAME
20
+
21
+ # Exit if the package is not installed
22
+ [ -x "$IPTABLES_RESTORE" ] || exit 0
23
+
24
+ # Exit if no rules
25
+ [ -f "$IPTABLES_STATE" ] || exit 0
26
+
27
+ # Load the VERBOSE setting and other rcS variables
28
+ . /lib/init/vars.sh
29
+
30
+ # Define LSB log_* functions.
31
+ # Depend on lsb-base (>= 3.2-14) to ensure that this file is present
32
+ # and status_of_proc is working.
33
+ . /lib/lsb/init-functions
34
+
35
+ case "$1" in
36
+ start|restart|force-reload)
37
+ log_daemon_msg "Starting $DESC" "$NAME"
38
+ $IPTABLES_RESTORE < $IPTABLES_STATE
39
+ case "$?" in
40
+ 0|1) log_end_msg 0 ;;
41
+ 2) log_end_msg 1 ;;
42
+ esac
43
+ ;;
44
+ *)
45
+ echo "Usage: $SCRIPTNAME {start|restart|force-reload}" >&2
46
+ exit 3
47
+ ;;
48
+ esac
@@ -0,0 +1,16 @@
1
+ partition "tootyfruity" do
2
+ label "apple", :address => "192.168.0.1"
3
+ label "blueberry", :address => "192.168.0.2"
4
+ label "cranberry", :address => "192.168.0.3"
5
+ label "eggplant", :address => "192.168.0.4"
6
+ label "fennel", :address => "192.168.0.5"
7
+ label "grapefruit", :address => "192.168.0.6"
8
+
9
+ accept "fruits of the forrest" do
10
+ protocols "tcp"
11
+ ports 22
12
+ from %w(apple blueberry cranberry eggplant fennel grapefruit)
13
+ to %w(apple blueberry cranberry eggplant fennel grapefruit)
14
+ end
15
+ end
16
+
@@ -0,0 +1,13 @@
1
+ partition "keepalived" do
2
+ label "primary lvs", :address => "172.16.0.216"
3
+ label "secondary lvs", :address => "172.16.0.217"
4
+ label "fw multicast", :address => "224.0.0.0/8"
5
+
6
+ accept "keepalive chatter on the fw multicast" do
7
+ protocols "tcp"
8
+ ports 80, 8600..8900
9
+ from "primary lvs", "secondary lvs"
10
+ to "fw multicast"
11
+ end
12
+ end
13
+
@@ -0,0 +1,14 @@
1
+ partition "keepalived" do
2
+ label "foobar-lvs-04", :address => "192.168.0.76"
3
+ label "util-01", :address => "172.16.0.246"
4
+ label "util-02", :address => "172.16.0.247"
5
+
6
+ accept "ssh access between lvs/firewalls" do
7
+ interface "vlan+"
8
+ protocols "tcp"
9
+ ports 22
10
+ from "foobar-lvs-04", "util-01", "util-02"
11
+ to "foobar-lvs-04"
12
+ end
13
+ end
14
+