Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Proof of concept

  • Loading branch information...
commit e38b624de79a702ff80247999510dd9db9ba3718 0 parents
@rodjek authored
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Tim Sharpe
+
+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:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+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.
0  README.md
No changes.
6 Rakefile
@@ -0,0 +1,6 @@
+require 'rake'
+require 'rspec/core/rake_task'
+
+task :default => :test
+
+RSpec::Core::RakeTask.new(:test)
22 iptables.gemspec
@@ -0,0 +1,22 @@
+Gem::Specification.new do |s|
+ s.name = 'iptables'
+ s.version = '0.0.1'
+ s.homepage = 'https://github.com/rodjek/iptables/'
+ s.summary = 'A Ruby DSL for creating iptables rules'
+ s.description = <<-EOS.undent
+ A Ruby DSL for creating iptables rules.
+ EOS
+
+ s.files = [
+ 'iptables.gemspec',
+ 'Rakefile',
+ 'README.md',
+ 'lib/iptables.rb',
+ 'spec/spec_helper.rb',
+ ]
+
+ s.add_development_dependency 'rspec'
+
+ s.authors = ['Tim Sharpe']
+ s.email = 'tim@sharpe.id.au'
+end
5 lib/iptables.rb
@@ -0,0 +1,5 @@
+require 'iptables/protocol/tcp'
+require 'iptables/rule'
+
+module IPTables
+end
53 lib/iptables/protocol/tcp.rb
@@ -0,0 +1,53 @@
+module IPTables
+ module Protocol
+ module TCP
+ attr_accessor :destination_port, :tcp_flags, :syn, :tcp_option, :mss
+ attr_reader :source_port
+
+ def self.extended(base)
+ base.mod_opts[:protocol] = []
+ end
+
+ def source_port=(value)
+ value = value.to_i
+ unless value > 0
+ raise "TCP source port must be a valid integer greater than 0"
+ end
+
+ @source_port = value
+ @mod_opts[:protocol] << "--sport" << source_port
+ end
+
+ def destination_port=(value)
+ value = value.to_i
+ unless value > 0
+ raise "TCP destination port must be a valid integer greater than 0"
+ end
+
+ @destination_port = value
+ @mod_opts[:protocol] << "--dport" << destination_port
+ end
+
+ def tcp_flags=(value)
+ unless value.is_a? Hash
+ raise "TCP flags must be a hash containing {:mask => [<flags>], :comp => [<flags>]}"
+ end
+
+ unless value.keys.include? :mask
+ raise "TCP flags hash must include the :mask flags"
+ end
+
+ unless value.keys.include? :comp
+ raise "TCP flags hash must include the :comp flags"
+ end
+
+ value[:not] == false if value[:not].nil?
+ @tcp_flags = value
+ @mod_opts[:protocol] << "--tcp-flags"
+ @mod_opts[:protocol] << "!" if tcp_flags[:not] == true
+ @mod_opts[:protocol] << tcp_flags[:mask].map { |r| r.to_s.upcase }.join(',')
+ @mod_opts[:protocol] << tcp_flags[:comp].map { |r| r.to_s.upcase }.join(',')
+ end
+ end
+ end
+end
59 lib/iptables/rule.rb
@@ -0,0 +1,59 @@
+module IPTables
+ class Rule
+ attr_accessor :chain, :source, :destination, :target, :in_interface, :out_interface, :modules
+ attr_reader :protocol
+ attr_accessor :mod_opts
+
+ def initialize
+ @mod_opts = {}
+ end
+
+ def protocol=(value)
+ protocols = {
+ :tcp => IPTables::Protocol::TCP,
+ }
+
+ value = value.to_sym unless value.is_a? Symbol
+ @protocol = value
+ begin
+ self.extend(protocols[value.to_sym])
+ rescue
+ raise "Unknown protocol '#{value.to_s}'"
+ end
+ end
+
+ def to_iptables
+ data = []
+ chain = @chain.to_s.upcase
+ data << "-A" << chain
+
+ if ['INPUT', 'FORWARD', 'PREROUTING'].include? chain
+ unless @in_interface.nil?
+ data << "-i" << @in_interface
+ end
+ end
+
+ if ['OUTPUT', 'FORWORD', 'POSTROUTING'].include? chain
+ unless @out_interface.nil?
+ data << '-o' << @out_interface
+ end
+ end
+
+ unless @source.nil?
+ data << "-s" << @source
+ end
+
+ unless @destination.nil?
+ data << "-d" << @destination
+ end
+
+ unless @protocol.nil?
+ data << "-p" << @protocol
+ @mod_opts[:protocol].each { |r| data << r }
+ end
+
+ data << "-j" << @target.to_s.upcase
+ data.join(' ')
+ end
+ end
+end
90 spec/iptables/protocol_tcp_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe IPTables::Rule do
+ describe "when protocol is tcp" do
+ subject do
+ rule = IPTables::Rule.new
+ rule.protocol = :tcp
+ rule
+ end
+
+ its(:protocol) { should == :tcp }
+
+ it { should respond_to(:source_port).with(0).arguments }
+ it { should respond_to(:source_port=).with(1).argument }
+ it { should respond_to(:destination_port).with(0).arguments }
+ it { should respond_to(:destination_port=).with(1).argument }
+ it { should respond_to(:tcp_flags).with(0).arguments }
+ it { should respond_to(:tcp_flags=).with(1).argument }
+ it { should respond_to(:syn).with(0).arguments }
+ it { should respond_to(:syn=).with(1).arguments }
+ it { should respond_to(:tcp_option).with(0).arguments }
+ it { should respond_to(:tcp_option=).with(1).argument }
+ it { should respond_to(:mss).with(0).arguments }
+ it { should respond_to(:mss=).with(1).argument }
+
+ context "when creating a rule with source_port" do
+ subject do
+ rule = IPTables::Rule.new
+ rule.chain = :input
+ rule.target = :accept
+ rule.protocol = :tcp
+ rule.source_port = 80
+ rule
+ end
+
+ its(:source_port) { should == 80 }
+ its(:to_iptables) {
+ should == "-A INPUT -p tcp --sport 80 -j ACCEPT"
+ }
+ end
+
+ context "when creating a rule with destination_port" do
+ subject do
+ rule = IPTables::Rule.new
+ rule.chain = :input
+ rule.target = :accept
+ rule.protocol = :tcp
+ rule.destination_port = 443
+ rule
+ end
+
+ its(:destination_port) { should == 443 }
+ its(:to_iptables) {
+ should == "-A INPUT -p tcp --dport 443 -j ACCEPT"
+ }
+ end
+
+ context "when creating a rule with tcp_flags" do
+ subject do
+ rule = IPTables::Rule.new
+ rule.chain = :input
+ rule.target = :accept
+ rule.protocol = :tcp
+ rule.tcp_flags = {:mask => [:syn, :rst, :ack, :fin], :comp => [:syn]}
+ rule
+ end
+
+ its(:tcp_flags) { should == {:mask => [:syn, :rst, :ack, :fin], :comp => [:syn]} }
+ its(:to_iptables) {
+ should == "-A INPUT -p tcp --tcp-flags SYN,RST,ACK,FIN SYN -j ACCEPT"
+ }
+ end
+
+ context "when creating a rule with tcp_flags inverted" do
+ subject do
+ rule = IPTables::Rule.new
+ rule.chain = :input
+ rule.target = :accept
+ rule.protocol = :tcp
+ rule.tcp_flags = {:mask => [:syn, :rst, :ack, :fin], :comp => [:syn], :not => true}
+ rule
+ end
+
+ its(:tcp_flags) { should == {:mask => [:syn, :rst, :ack, :fin], :comp => [:syn], :not => true} }
+ its(:to_iptables) {
+ should == "-A INPUT -p tcp --tcp-flags ! SYN,RST,ACK,FIN SYN -j ACCEPT"
+ }
+ end
+ end
+end
43 spec/iptables/rule_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe IPTables::Rule do
+ context "responds to" do
+ subject { described_class.new }
+
+ it { should respond_to(:chain).with(0).arguments }
+ it { should respond_to(:chain=).with(1).argument }
+ it { should respond_to(:protocol).with(0).arguments }
+ it { should respond_to(:protocol=).with(1).argument }
+ it { should respond_to(:source).with(0).arguments }
+ it { should respond_to(:source=).with(1).argument }
+ it { should respond_to(:destination).with(0).arguments }
+ it { should respond_to(:destination=).with(1).argument }
+ it { should respond_to(:target).with(0).arguments }
+ it { should respond_to(:target=).with(1).argument }
+ it { should respond_to(:in_interface).with(0).arguments }
+ it { should respond_to(:in_interface=).with(1).argument }
+ it { should respond_to(:out_interface).with(0).arguments }
+ it { should respond_to(:out_interface=).with(1).argument }
+ it { should respond_to(:modules).with(0).arguments }
+ it { should respond_to(:modules=).with(1).argument }
+ end
+
+ context "when generating a simple rule to allow inbound traffic from 192.168.0.1 to 192.168.0.2" do
+ subject do
+ rule = IPTables::Rule.new
+ rule.chain = :input
+ rule.source = '192.168.0.1'
+ rule.destination = '192.168.0.2'
+ rule.target = :accept
+ rule
+ end
+
+ its(:chain) { should == :input }
+ its(:source) { should == '192.168.0.1' }
+ its(:destination) { should == '192.168.0.2' }
+ its(:target) { should == :accept }
+ its(:to_iptables) {
+ should == "-A INPUT -s 192.168.0.1 -d 192.168.0.2 -j ACCEPT"
+ }
+ end
+end
5 spec/spec_helper.rb
@@ -0,0 +1,5 @@
+RSpec.configure do |config|
+ config.mock_with :rspec
+end
+
+require 'iptables'
Please sign in to comment.
Something went wrong with that request. Please try again.