puffy 1.0.0 → 1.1.0.pre.rc1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b05f6a865607d5ece6a83d81f3ef925decf281b4fa0fc54daf7aaa2213135831
4
- data.tar.gz: 9ac1891cb02cf876b0ac64fc19dc857996c1e3dfa3141e5b339c82f98603ced9
3
+ metadata.gz: 212bbb0b9badf3e5595984f60e85f1739c907434fa65b6498279803111499fd7
4
+ data.tar.gz: 148f4d57788cc7db0fdee92a6758be60c698a8d59f76e89804875f5424ab52e2
5
5
  SHA512:
6
- metadata.gz: 73c1d65f67292e4e3d2452978ad21bc2bca7001cce5304472686a8a843bf742e635b11c86606f43a1af1024cb30bf63faf7f1444cc4b380ee7f589300086bb2b
7
- data.tar.gz: f93e5ffc88eda099624f28ce13997dbe0a9222955eb76aa6e5e725d77be3a313da0b38ccddf3f10a550f60418cb44e39bb3724e2a02aab952c2a6b643ec7d28c
6
+ metadata.gz: 96a3238a90ebb5625ad18279b9b72ec1f122cde54463a6377238091a5e4a5ac8ebb6488fd0de0d176b2b73589e41991eecb53c824c26d5d62f8e7f5c17300d07
7
+ data.tar.gz: 97a3e613aa975b7e0828bc467f5d485b81eb24eef6771cc90927f3fa6dfdedfa3a142de61ac22c3952c8a042b41788b18270100df0639102de2fd4441a157c36
@@ -14,7 +14,7 @@ jobs:
14
14
  rubocop:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v6
18
18
  - name: Setup ruby
19
19
  uses: ruby/setup-ruby@v1
20
20
  with:
@@ -36,7 +36,7 @@ jobs:
36
36
  - "3.3"
37
37
  name: Ruby ${{ matrix.ruby }}
38
38
  steps:
39
- - uses: actions/checkout@v4
39
+ - uses: actions/checkout@v6
40
40
  - name: Setup ruby
41
41
  uses: ruby/setup-ruby@v1
42
42
  with:
@@ -45,12 +45,4 @@ jobs:
45
45
  - name: Generate the parser
46
46
  run: bundle exec rake gen_parser
47
47
  - name: Run tests without uploading code coverage
48
- if: ${{ matrix.ruby != '3.0' }}
49
48
  run: bundle exec rake
50
- - name: Run tests and upload coverage to Code Climate
51
- if: ${{ matrix.ruby == '3.0' }}
52
- uses: paambaati/codeclimate-action@v5.0.0
53
- env:
54
- CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE_TOKEN }}
55
- with:
56
- coverageCommand: bundle exec rake
data/.rubocop.yml CHANGED
@@ -7,6 +7,10 @@ AllCops:
7
7
  - tmp/**/*.rb
8
8
  - vendor/bundle/**/*
9
9
 
10
+ plugins:
11
+ - rubocop-rake
12
+ - rubocop-rspec
13
+
10
14
  Layout/HashAlignment:
11
15
  EnforcedColonStyle: table
12
16
  EnforcedHashRocketStyle: table
@@ -26,6 +30,16 @@ Metrics/ModuleLength:
26
30
  Exclude:
27
31
  - spec/**/*.rb
28
32
 
33
+ RSpec/ExampleLength:
34
+ Max: 50
35
+
36
+ RSpec/MultipleExpectations:
37
+ Max: 10
38
+
39
+ RSpec/SpecFilePathFormat:
40
+ Exclude:
41
+ - spec/core_ext_spec.rb
42
+
29
43
  Style/Documentation:
30
44
  Exclude:
31
45
  - features/support/env.rb
data/README.md CHANGED
@@ -25,6 +25,13 @@ Rules must appear in either a *node* or *service* definition, *services* being
25
25
  reusable blocks of related rules:
26
26
 
27
27
  ~~~
28
+ policy block in all
29
+ policy block out log all
30
+
31
+ service dns do
32
+ pass proto {tcp udp} to port domain
33
+ end
34
+
28
35
  service ntp do
29
36
  pass proto udp to port ntp
30
37
  end
@@ -42,6 +49,7 @@ service www do
42
49
  end
43
50
 
44
51
  service base do
52
+ client dns
45
53
  client ntp
46
54
  server ssh
47
55
  end
data/Rakefile CHANGED
@@ -22,7 +22,11 @@ task test: %i[spec features]
22
22
 
23
23
  task default: :test
24
24
 
25
+ # rubocop:disable Rake/Desc
26
+ task feature: :gen_parser
25
27
  task build: :gen_parser
28
+ task spec: :gen_parser
29
+ # rubocop:enable Rake/Desc
26
30
 
27
31
  desc 'Generate the puffy language parser'
28
32
  task gen_parser: 'lib/puffy/parser.tab.rb'
data/lib/puffy/cli.rb CHANGED
@@ -49,10 +49,10 @@ module Puffy
49
49
  run do |opts, args|
50
50
  parser = cli.load_network(args[:network])
51
51
  rules = parser.ruleset_for(args[:hostname])
52
- policy = parser.policy_for(args[:hostname])
52
+ policies = parser.policies_for(args[:hostname])
53
53
 
54
54
  formatter = Object.const_get("Puffy::Formatters::#{opts[:formatter]}::Ruleset").new
55
- puts formatter.emit_ruleset(rules, policy)
55
+ puts formatter.emit_ruleset(rules, policies)
56
56
  end
57
57
  end
58
58
 
@@ -34,12 +34,12 @@ module Puffy
34
34
  ["# Generated by puffy v#{Puffy::VERSION} on #{Time.now.strftime('%c')}"]
35
35
  end
36
36
 
37
- # Returns a String representation of the provided +rules+ Array of Puffy::Rule with the +policy+ policy.
37
+ # Returns a String representation of the provided +rules+ Array of Puffy::Rule with the +policies+ policies.
38
38
  #
39
39
  # @param rules [Array<Puffy::Rule>] array of Puffy::Rule.
40
- # @param _policy [Symbol] ruleset policy.
40
+ # @param _policies [Symbol] ruleset policies.
41
41
  # @return [String]
42
- def emit_ruleset(rules, _policy = nil)
42
+ def emit_ruleset(rules, _policies = nil)
43
43
  rules.collect { |rule| @rule_formatter.emit_rule(rule) }.join("\n")
44
44
  end
45
45
 
@@ -27,13 +27,13 @@ module Puffy
27
27
  }
28
28
  end
29
29
 
30
- # Returns a Iptables String representation of the provided +rules+ Array of Puffy::Rule with the +policy+ policy.
31
- def emit_ruleset(rules, policy = :block)
30
+ # Returns a Iptables String representation of the provided +rules+ Array of Puffy::Rule with the +policies+ policies.
31
+ def emit_ruleset(rules, policies)
32
32
  parts = []
33
33
  parts << emit_header
34
34
  parts << raw_ruleset(raw_rules(rules))
35
35
  parts << nat_ruleset(nat_rules(rules))
36
- parts << filter_ruleset(filter_rules(rules), policy)
36
+ parts << filter_ruleset(filter_rules(rules), policies)
37
37
  ruleset = parts.flatten.compact.join("\n")
38
38
  "#{ruleset}\n"
39
39
  end
@@ -61,14 +61,14 @@ module Puffy
61
61
  parts
62
62
  end
63
63
 
64
- def filter_ruleset(rules, policy)
64
+ def filter_ruleset(rules, policies)
65
65
  return unless rules.any?
66
66
 
67
67
  parts = ['*filter']
68
- parts << emit_chain_policies(input: policy, forward: policy, output: policy)
69
- parts << input_filter_ruleset(rules)
70
- parts << forward_filter_ruleset(rules)
71
- parts << output_filter_rulset(rules)
68
+ parts << emit_chain_policies(input: policies.dig(:in, :action), forward: policies.dig(:in, :action), output: policies.dig(:out, :action))
69
+ parts << input_filter_ruleset(rules, policies)
70
+ parts << forward_filter_ruleset(rules, policies)
71
+ parts << output_filter_rulset(rules, policies)
72
72
  parts << 'COMMIT'
73
73
  parts
74
74
  end
@@ -77,20 +77,32 @@ module Puffy
77
77
  policies.map { |chain, action| ":#{chain.upcase} #{Puffy::Formatters::Iptables.iptables_action(action)} [0:0]" }
78
78
  end
79
79
 
80
- def input_filter_ruleset(rules)
81
- parts = ['-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT']
80
+ def input_filter_ruleset(rules, policies)
81
+ parts = [
82
+ '-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT',
83
+ '-A INPUT -m conntrack --ctstate INVALID -j DROP',
84
+ ]
82
85
  parts << input_filter_rules(rules).map { |rule| @rule_formatter.emit_rule(rule) }
86
+ parts << '-A INPUT -j LOG' if policies.dig(:in, :log)
87
+ parts
83
88
  end
84
89
 
85
- def forward_filter_ruleset(rules)
86
- parts = ['-A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT']
87
- parts << rules.select(&:fwd?).map { |rule| @rule_formatter.emit_rule(rule) }
88
- parts << rules.select { |r| r.rdr? && !Puffy::Formatters::Base.loopback_addresses.include?(r.rdr_to_host) }.map { |rule| @rule_formatter.emit_rule(Puffy::Rule.fwd_rule(rule)) }
90
+ def forward_filter_ruleset(rules, policies)
91
+ parts = [
92
+ '-A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT',
93
+ '-A FORWARD -m conntrack --ctstate INVALID -j DROP',
94
+ ]
95
+ parts << fwd_filter_rules(rules).map { |rule| @rule_formatter.emit_rule(rule) }
96
+ parts << rdr_filter_rules(rules).map { |rule| @rule_formatter.emit_rule(Puffy::Rule.fwd_rule(rule)) }
97
+ parts << '-A FORWARD -j LOG' if policies.dig(:in, :log)
98
+ parts
89
99
  end
90
100
 
91
- def output_filter_rulset(rules)
101
+ def output_filter_rulset(rules, policies)
92
102
  parts = ['-A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT']
93
103
  parts << output_filter_rules(rules).map { |rule| @rule_formatter.emit_rule(rule) }
104
+ parts << '-A OUTPUT -j LOG' if policies.dig(:out, :log)
105
+ parts
94
106
  end
95
107
 
96
108
  def raw_rules(rules)
@@ -113,6 +125,14 @@ module Puffy
113
125
  end
114
126
  end
115
127
 
128
+ def fwd_filter_rules(rules)
129
+ rules.select(&:fwd?)
130
+ end
131
+
132
+ def rdr_filter_rules(rules)
133
+ rules.select { |r| r.rdr? && !Puffy::Formatters::Base.loopback_addresses.include?(r.rdr_to_host) }
134
+ end
135
+
116
136
  def output_filter_rules(rules)
117
137
  rules.select { |r| r.filter? && r.out? }.map do |rule|
118
138
  dup = rule.dup
@@ -5,9 +5,9 @@ module Puffy
5
5
  module Iptables4 # :nodoc:
6
6
  # IPv4 Iptables implementation of a Puffy Ruleset formatter.
7
7
  class Ruleset < Puffy::Formatters::Iptables::Ruleset # :nodoc:
8
- # Return an IPv4 Iptables String representation of the provided +rules+ Puffy::Rule with the +policy+ policy.
9
- def emit_ruleset(rules, policy = :block)
10
- super(rules.select(&:ipv4?), policy)
8
+ # Return an IPv4 Iptables String representation of the provided +rules+ Puffy::Rule with the +policies+ policies.
9
+ def emit_ruleset(rules, policies)
10
+ super(rules.select(&:ipv4?), policies)
11
11
  end
12
12
 
13
13
  def filename_fragment
@@ -5,9 +5,9 @@ module Puffy
5
5
  module Iptables6 # :nodoc:
6
6
  # IPv6 Iptables implementation of a Puffy Ruleset formatter.
7
7
  class Ruleset < Puffy::Formatters::Iptables::Ruleset # :nodoc:
8
- # Return an IPv6 Iptables String representation of the provided +rules+ Puffy::Rule with the +policy+ policy.
9
- def emit_ruleset(rules, policy = :block)
10
- super(rules.select(&:ipv6?), policy)
8
+ # Return an IPv6 Iptables String representation of the provided +rules+ Puffy::Rule with the +policies+ policies.
9
+ def emit_ruleset(rules, policies)
10
+ super(rules.select(&:ipv6?), policies)
11
11
  end
12
12
 
13
13
  def filename_fragment
@@ -6,10 +6,10 @@ module Puffy
6
6
  # Pf implementation of a Puffy Ruleset formatter.
7
7
  class Ruleset < Puffy::Formatters::Base::Ruleset # :nodoc:
8
8
  # Returns a Pf String representation of the provided +rules+ Array of Puffy::Rule.
9
- def emit_ruleset(rules, policy = :block)
9
+ def emit_ruleset(rules, policies)
10
10
  parts = []
11
11
 
12
- parts << emit_header(policy)
12
+ parts << emit_header(policies)
13
13
 
14
14
  parts << super(rules.select(&:nat?))
15
15
  parts << super(rules.select(&:rdr?))
@@ -23,12 +23,14 @@ module Puffy
23
23
  ['pf', 'pf.conf']
24
24
  end
25
25
 
26
- def emit_header(policy)
26
+ def emit_header(policies)
27
27
  parts = super()
28
28
  parts << 'match in all scrub (no-df)'
29
29
  parts << 'set skip on lo'
30
- parts << @rule_formatter.emit_rule(Puffy::Rule.new(action: policy, dir: :in, no_quick: true))
31
- parts << @rule_formatter.emit_rule(Puffy::Rule.new(action: policy, dir: :out, no_quick: true))
30
+ %i[in out].each do |direction|
31
+ policy = policies[direction]
32
+ parts << @rule_formatter.emit_rule(Puffy::Rule.new(action: policy[:action], log: policy[:log], dir: direction, no_quick: true))
33
+ end
32
34
  parts
33
35
  end
34
36
  end
@@ -40,6 +42,7 @@ module Puffy
40
42
  parts = []
41
43
  parts << emit_action(rule)
42
44
  parts << emit_direction(rule)
45
+ parts << emit_log(rule)
43
46
  parts << emit_quick(rule)
44
47
  parts << emit_on(rule)
45
48
  parts << emit_what(rule)
@@ -62,6 +65,10 @@ module Puffy
62
65
  'quick' unless rule.no_quick
63
66
  end
64
67
 
68
+ def emit_log(rule)
69
+ 'log' if rule.log
70
+ end
71
+
65
72
  def emit_on(rule)
66
73
  "on #{rule.on.gsub('!', '! ')}" if rule.on
67
74
  end