ript 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
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
+