catflap 0.0.2 → 1.0.1

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.
@@ -0,0 +1,104 @@
1
+ require 'catflap/plugins/firewall/plugin'
2
+ require 'netfilter/writer'
3
+ include NetfilterWriter
4
+
5
+ ##
6
+ # A firewall plugin driver to implement rules on the NetFilter filter table.
7
+ #
8
+ # This driver passes rules to the filter table via the iptables user-space
9
+ # client.Two new chains are installed in the filter table, named by default:
10
+ # CATFLAP-DENY and CATFLAP-ALLOW. These are installed in the INPUT chain. You
11
+ # can configure the driver to install a LOG to log rejected packets, and also
12
+ # whether to REJECT or DROP denied packets.
13
+ #
14
+ # If you want to redirect to the Catflap server login port instead, then you
15
+ # should use the 'netfilter' driver instead, which is the default.
16
+ #
17
+ # @example firewall: plugin: 'iptables'
18
+ #
19
+ # @author Nyk Cowham <nykcowham@gmail.com>
20
+ class IptablesDriver < FirewallPlugin
21
+ # Initialize the driver class.
22
+ # @param [Hash<String, Hash>] config built by Catflap::initialize_config()
23
+ # @param [Boolean] noop set true to not send commands to firewall client.
24
+ # @param [Boolean] verbose set true to print command output to stdout stream.
25
+ # @return void
26
+ def initialize(config, noop, verbose)
27
+ super
28
+ @chain = config['firewall']['options']['chain'] || 'CATFLAP'
29
+ @log_rejected = config['firewall']['options']['log_rejected'] || false
30
+ @accept_local = config['firewall']['options']['accept_local'] || false
31
+ @policy = config['firewall']['options']['reject_policy'].to_sym || :drop
32
+ @allow = @chain + '-ALLOW'
33
+ @deny = @chain + '-DENY'
34
+ @r = Rules.new(:filter, @dports)
35
+ @r.match('multiport')
36
+ @r.noop = noop
37
+ @r.verbose = verbose
38
+ end
39
+
40
+ # Method to install the driver rules into iptables.
41
+ # @return void
42
+ # @raise StandardError when iptables reports an error.
43
+ def install_rules
44
+ target = (@policy == :reject) ? 'REJECT' : 'DROP'
45
+ log = @log_rejected
46
+ local = @accept_local
47
+ @r.chain(:new, @allow)
48
+ .chain(:new, @deny)
49
+ .rule(:add, chain: 'INPUT', jump: @allow)
50
+ .rule(:add, chain: 'INPUT', jump: @deny)
51
+ .rule(:add, chain: @deny, jump: 'LOG') { log }
52
+ .rule(:add, chain: @deny, jump: target)
53
+ .rule(:add, chain: @allow, jump: 'ACCEPT',
54
+ src: 'localhost') { local }
55
+ .do
56
+ end
57
+
58
+ # Method to uninstall the driver rules from iptables.
59
+ # @return void
60
+ # @raise StandardError when iptables reports an error.
61
+ def uninstall_rules
62
+ @r.rule(:delete, chain: 'INPUT', jump: @allow)
63
+ .rule(:delete, chain: 'INPUT', jump: @deny)
64
+ .chain(:flush, @allow)
65
+ .chain(:delete, @allow)
66
+ .chain(:flush, @deny)
67
+ .chain(:delete, @deny)
68
+ .do
69
+ end
70
+
71
+ # Method to purge all rules from CATFLAP-ALLOW chain.
72
+ # @return void
73
+ # @raise StandardError when iptables reports an error.
74
+ def purge_rules
75
+ @r.chain(:flush, @allow).do
76
+ end
77
+
78
+ # Method to list rules in CATFLAP-ALLOW chain.
79
+ # @return void
80
+ # @raise StandardError when iptables reports an error.
81
+ def list_rules
82
+ @r.chain(:list, @allow).do
83
+ end
84
+
85
+ # Method to check the CATFLAP-ALLOW chain for an allowed IP.
86
+ # @return [Boolean] true indicates that IP address already has access.
87
+ def check_address(ip)
88
+ @r.rule(:check, src: ip, chain: @allow, jump: 'ACCEPT').do?
89
+ end
90
+
91
+ # Method to add/grant an IP access in the CATFLAP-ALLOW chain.
92
+ # @return void
93
+ # @raise StandardError when iptables reports an error.
94
+ def add_address(ip)
95
+ @r.rule(:insert, src: ip, chain: @allow, jump: 'ACCEPT').do
96
+ end
97
+
98
+ # Method to delete/revoke access for an IP in the CATFLAP-ALLOW chain.
99
+ # @return void
100
+ # @raise StandardError when iptables reports an error.
101
+ def delete_address(ip)
102
+ @r.rule(:delete, src: ip, chain: @allow, jump: 'ACCEPT').do
103
+ end
104
+ end
@@ -0,0 +1,114 @@
1
+ require 'catflap/plugins/firewall/plugin'
2
+ require 'netfilter/writer'
3
+ include NetfilterWriter
4
+
5
+ ##
6
+ # A firewall plugin driver to implement rules on the NetFilter NAT table.
7
+ #
8
+ # This driver passes rules to the NAT table via the iptables user-space client.
9
+ # Two new chains are installed in the NAT table, named by default:
10
+ # CATFLAP-DENY and CATFLAP-ALLOW. These are installed in the PREROUTING chain.
11
+ # You can configure the driver to install a LOG to log denied packets.
12
+ #
13
+ # This is the default and recommended driver for Linux systems running iptables.
14
+ #
15
+ # @example firewall: plugin: 'netfilter'
16
+ #
17
+ # @author Nyk Cowham <nykcowham@gmail.com>
18
+ class NetfilterDriver < FirewallPlugin
19
+ # Initialize the driver class.
20
+ # @param [Hash<String, Hash>] config hash: Catflap::initialize_config()
21
+ # @param [Boolean] noop send the commands to the firewall client.
22
+ # @param [Boolean] verbose print the command output to stdout stream.
23
+ # @return void
24
+ def initialize(config, noop = false, verbose = false)
25
+ super
26
+ @chain = config['firewall']['options']['chain'] || 'CATFLAP'
27
+ @forward = config['firewall']['options']['forward']
28
+ @log_rejected = config['firewall']['options']['log_rejected'] || false
29
+ @accept_local = config['firewall']['options']['accept_local'] || false
30
+ @allow = @chain + '-ALLOW'
31
+ @deny = @chain + '-DENY'
32
+ @r = Rules.new(:nat, @dports)
33
+ @r.match('multiport')
34
+ @r.noop = noop
35
+ @r.verbose = verbose
36
+ end
37
+
38
+ # Method to install the driver rules into iptables.
39
+ # @return void
40
+ # @raise StandardError when iptables reports an error.
41
+ def install_rules
42
+ # We must make these local variables, so they are exposed to the blocks.
43
+ log = @log_rejected
44
+ deny_local = !@accept_local
45
+
46
+ # Create a new chain on the NAT table for our catflap netfilter allow rules.
47
+ @r.chain(:new, @allow)
48
+ .chain(:new, @deny)
49
+ .rule(:add, chain: 'PREROUTING', jump: @allow)
50
+ .rule(:add, chain: 'PREROUTING', jump: @deny)
51
+ .rule(:add, chain: @deny, jump: 'LOG') { log }
52
+ .rule(:add, chain: 'OUTPUT', out: 'lo', jump: @allow) { deny_local }
53
+ .rule(:add, chain: 'OUTPUT', out: 'lo', jump: @deny) { deny_local }
54
+
55
+ @forward.each do |src, dest|
56
+ src = src.to_s
57
+ @r.rule(:add, chain: @deny, jump: 'REDIRECT', dports: src, to_port: dest)
58
+ end
59
+ @r.do
60
+ end
61
+
62
+ # Method to uninstall the driver rules from iptables.
63
+ # @return void
64
+ # @raise StandardError when iptables reports an error.
65
+ def uninstall_rules
66
+ deny_local = !@accept_local
67
+
68
+ @r.rule(:delete, chain: 'PREROUTING', jump: @allow)
69
+ .rule(:delete, chain: 'PREROUTING', jump: @deny)
70
+ .rule(:delete, chain: 'OUTPUT', out: 'lo',
71
+ jump: @allow) { deny_local }
72
+ .rule(:delete, chain: 'OUTPUT', out: 'lo',
73
+ jump: @deny) { deny_local }
74
+ .chain(:flush, @allow)
75
+ .chain(:flush, @deny)
76
+ .chain(:delete, @allow)
77
+ .chain(:delete, @deny)
78
+ .do
79
+ end
80
+
81
+ # Method to purge all rules from CATFLAP-ALLOW chain.
82
+ # @return void
83
+ # @raise StandardError when iptables reports an error.
84
+ def purge_rules
85
+ @r.chain(:flush, @allow).do
86
+ end
87
+
88
+ # Method to list rules in CATFLAP-ALLOW chain.
89
+ # @return void
90
+ # @raise StandardError when iptables reports an error.
91
+ def list_rules
92
+ @r.chain(:list, @allow).do
93
+ end
94
+
95
+ # Method to check the CATFLAP-ALLOW chain for an allowed IP.
96
+ # @return [Boolean] true indicates that IP address already has access.
97
+ def check_address(ip)
98
+ @r.rule(:check, src: ip, chain: @allow, jump: 'ACCEPT').do?
99
+ end
100
+
101
+ # Method to add/grant an IP access in the CATFLAP-ALLOW chain.
102
+ # @return void
103
+ # @raise StandardError when iptables reports an error.
104
+ def add_address(ip)
105
+ @r.rule(:insert, src: ip, chain: @allow, jump: 'ACCEPT').do
106
+ end
107
+
108
+ # Method to delete/revoke access for an IP in the CATFLAP-ALLOW chain.
109
+ # @return void
110
+ # @raise StandardError when iptables reports an error.
111
+ def delete_address(ip)
112
+ @r.rule(:delete, src: ip, chain: @allow, jump: 'ACCEPT').do
113
+ end
114
+ end
@@ -0,0 +1,67 @@
1
+ # Abstract class plugin driver to implement rules on the NetFilter filter table.
2
+ #
3
+ # Firewall drivers should inherit from this abstract class and implement each
4
+ # method. This serves as a contract for firewalls to follow to ensure that the
5
+ # driver can respond to firewall calls.
6
+ #
7
+ # @example firewall: plugin: 'iptables'
8
+ #
9
+ # @author Nyk Cowham <nykcowham@gmail.com>
10
+ class FirewallPlugin
11
+ attr_reader :dports, :catflap_port
12
+
13
+ def initialize(config, noop = false, verbose = false)
14
+ @noop = noop
15
+ @verbose = verbose
16
+ @catflap_port = config['server']['port']
17
+ @dports = config['firewall']['dports']
18
+ end
19
+
20
+ # Implement method to install the driver rules into iptables.
21
+ # @return void
22
+ # @raise StandardError when iptables reports an error.
23
+ def install_rules
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # Implement method to uninstall the driver rules from iptables.
28
+ # @return void
29
+ # @raise StandardError when iptables reports an error.
30
+ def uninstall_rules
31
+ raise NotImplementedError
32
+ end
33
+
34
+ # Implement method to purge all rules from CATFLAP-ALLOW chain.
35
+ # @return void
36
+ # @raise StandardError when iptables reports an error.
37
+ def purge_rules
38
+ raise NotImplementedError
39
+ end
40
+
41
+ # Implement method to list rules in CATFLAP-ALLOW chain.
42
+ # @return void
43
+ # @raise StandardError when iptables reports an error.
44
+ def list_rules
45
+ raise NotImplementedError
46
+ end
47
+
48
+ # Implement method to check the CATFLAP-ALLOW chain for an allowed IP.
49
+ # @return [Boolean] true indicates that IP address already has access.
50
+ def check_address(_)
51
+ raise NotImplementedError
52
+ end
53
+
54
+ # Implement method to add/grant an IP access in the CATFLAP-ALLOW chain.
55
+ # @return void
56
+ # @raise StandardError when iptables reports an error.
57
+ def add_address(_)
58
+ raise NotImplementedError
59
+ end
60
+
61
+ # Implement method to delete/revoke access in the CATFLAP-ALLOW chain.
62
+ # @return void
63
+ # @raise StandardError when iptables reports an error.
64
+ def delete_address(_)
65
+ raise NotImplementedError
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ # This is the version bumper.
2
+ class Catflap
3
+ # Current version of Catflap gem.
4
+ VERSION = '1.0.1'
5
+ end
@@ -0,0 +1,125 @@
1
+ require 'catflap/firewall'
2
+ include Firewall
3
+
4
+ # Mixin module to add rule handling functions to netfilter-based drivers.
5
+ #
6
+ # @author Nyk Cowham <nykcowham@gmail.com>
7
+ module NetfilterWriter
8
+ # Class providing a DSL for defining netfilter rules
9
+ # @author Nyk Cowham <nykcowham@gmail.com>
10
+ class Rules
11
+ attr_accessor :noop, :verbose
12
+
13
+ def initialize(table, ports = nil)
14
+ @table = table
15
+ @ports = ports
16
+ @buffer = ''
17
+ end
18
+
19
+ def table(table)
20
+ @table = table
21
+ self
22
+ end
23
+
24
+ def ports(ports)
25
+ @ports = ports
26
+ self
27
+ end
28
+
29
+ def match(match)
30
+ @match = match
31
+ self
32
+ end
33
+
34
+ # Create, flush and delete chains
35
+ # @param [String] cmd the operation to perform (add, delete, flush)
36
+ # @param [String] chain name of the chain (e.g. INPUT, CATFLAP-DENY, etc.)
37
+ # @return self
38
+ def chain(cmd, chain, a = {})
39
+ cmds = {
40
+ new: '-N', rename: '-E', delete: '-X', flush: '-F',
41
+ list_rules: '-S', list: '-L', zero: '-Z', policy: '-P'
42
+ }
43
+ table = build_option('-t', @table)
44
+ numeric = build_option('-n', a[:numeric])
45
+ rulenum = build_option(true, a[:rulenum])
46
+ to = build_option(true, a[:to])
47
+ @buffer << [
48
+ 'iptables', table, numeric, cmds[cmd], chain, rulenum, to
49
+ ].compact.join(' ') << "\n"
50
+ self
51
+ end
52
+
53
+ # Create, flush and delete chains
54
+ # @param [String] cmd the operation to perform (add, delete, insert, etc.)
55
+ # @param [String] chain name of the chain (e.g. INPUT, CATFLAP-DENY, etc.)
56
+ # @return self
57
+ def rule(cmd, a, &block)
58
+ # Evaluate a block expression and return early if it evaluates to false.
59
+ # If no block is passed it is equivalent to the block: { true }.
60
+ return self if block_given? && !instance_eval(&block)
61
+
62
+ raise ArgumentError, 'chain is a required argument' unless a[:chain]
63
+ assert_valid_ipaddr(a[:src]) if a[:src]
64
+ assert_valid_ipaddr(a[:dst]) if a[:dst]
65
+
66
+ # Map of commands for rules
67
+ cmds = {
68
+ add: '-A', delete: '-D', insert: '-I', replace: '-R',
69
+ check: '-C'
70
+ }
71
+
72
+ a[:proto] ||= 'tcp'
73
+ table = build_option('-t', @table)
74
+ jump = build_option('-j', a[:jump])
75
+ goto = build_option('-g', a[:goto])
76
+ proto = build_option('-p', a[:proto])
77
+ inface = build_option('-i', a[:in])
78
+ outface = build_option('-o', a[:out])
79
+ src = build_option('-s', a[:src])
80
+ dst = build_option('-d', a[:dst])
81
+ match = build_option('-m', a[:match] || @match)
82
+ ports = build_option('--dport', @ports)
83
+ to_port = build_option('--to-port', a[:to_port])
84
+ @buffer << [
85
+ 'iptables', table, cmds[cmd], a[:chain], src, dst, outface,
86
+ inface, proto, match, ports, jump || goto, to_port
87
+ ].compact.join(' ') << "\n"
88
+ self
89
+ end
90
+ end
91
+
92
+ def build_option(flag, value)
93
+ return flag if value.is_a?(TrueClass)
94
+ return value if flag.is_a?(TrueClass)
95
+ return flag << ' ' << value.to_s if flag && value
96
+ end
97
+
98
+ # Add a raw text rule, (e.g.: iptables -t nat CATFLAP-ALLOW ...)
99
+ # @param [String] raw_rule custom raw iptables command.
100
+ # @return self
101
+ def raw(raw_rule)
102
+ @buffer = raw_rule
103
+ self
104
+ end
105
+
106
+ # Flush the rule buffer and output the resulting iptables commands.
107
+ # @return [String] rule text that can be sent iptables user-space client.
108
+ def flush
109
+ out = @buffer
110
+ @buffer = ''
111
+ out
112
+ end
113
+
114
+ # Flush the rule and execute in iptables user-space client.
115
+ # @return void
116
+ def do
117
+ execute flush
118
+ end
119
+
120
+ # Flush the rule and execute commands and return success/fail value.
121
+ # @return [Boolean] true if the execution was successful.
122
+ def do?
123
+ execute_true? flush
124
+ end
125
+ end
@@ -0,0 +1,44 @@
1
+
2
+ .centered {
3
+ margin: 0px auto;
4
+ display: block;
5
+ }
6
+
7
+ .hidden {
8
+ display: none;
9
+ }
10
+
11
+ input[type="text"]:focus.failed {
12
+ background-color: pink;
13
+ border-color: red;
14
+ color: black;
15
+ }
16
+
17
+ input[type="text"] {
18
+ display: block;
19
+ margin: 0px auto;
20
+ margin-top: 1em;
21
+ padding-left: 0.4em;
22
+ height: 1.7em;
23
+ width: 50%;
24
+ color: #999;
25
+ font-family: sans-serif;
26
+ font-size: 2.2em;
27
+ border: solid 2px #dcdcdc;
28
+ transition: box-shadow 0.3s, border 0.3s;
29
+ box-shadow: inset 1px 1px 2px 0 #707070;
30
+ appearance: none;
31
+ border-radius: 2px;
32
+ }
33
+
34
+ input[type="text"]:focus {
35
+ border: solid 2px #707070;
36
+ box-shadow: inset 1px 1px 2px 0 #c9c9c9;
37
+ }
38
+
39
+ .message {
40
+ width: 50%;
41
+ font-size: 2em;
42
+ margin-top: 1.5em;
43
+ color: #860000;
44
+ }