Skip to content

Commit f36f728

Browse files
committed
Add support for nftables
1 parent 22deeee commit f36f728

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

lib/puffy.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require 'puffy/formatters/iptables'
88
require 'puffy/formatters/iptables4'
99
require 'puffy/formatters/iptables6'
10+
require 'puffy/formatters/nftables'
1011
require 'puffy/formatters/pf'
1112
require 'puffy/puppet'
1213
require 'puffy/resolver'

lib/puffy/formatters/nftables.rb

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# frozen_string_literal: true
2+
3+
module Puffy
4+
module Formatters
5+
module Nftables # :nodoc:
6+
# Returns the rule action
7+
#
8+
# @return [String]
9+
def self.nftables_action(rule_or_action, ret: false)
10+
case rule_or_action
11+
when :pass then 'accept'
12+
when :block then ret ? 'return' : 'drop'
13+
when Puffy::Rule then nftables_action(rule_or_action.action, ret: rule_or_action.return)
14+
end
15+
end
16+
17+
# Nftables implementation of a Puffy Ruleset formatter.
18+
class Ruleset < Puffy::Formatters::Base::Ruleset
19+
alias parent_emit_ruleset emit_ruleset
20+
21+
# Returns a Nftables String representation of the provided +rules+ Array of Puffy::Rule.
22+
def emit_ruleset(rules, policy = :block)
23+
<<~FRAGMENT
24+
#!/usr/sbin/nft -f
25+
26+
flush ruleset
27+
28+
#{inet_filter(rules, policy)}
29+
FRAGMENT
30+
end
31+
32+
def inet_filter(rules, policy)
33+
<<~FRAGMENT.chomp
34+
table inet filter {
35+
#{chain_input(rules.select(&:in?), policy)}
36+
#{chain_forward(rules.select(&:fwd?))}
37+
#{chain_output(rules.select(&:out?), policy)}
38+
}
39+
FRAGMENT
40+
end
41+
42+
def chain_input(rules, policy)
43+
<<~FRAGMENT.chomp
44+
\tchain input {
45+
\t\ttype filter hook input priority 0;
46+
\t\tpolicy #{Nftables.nftables_action(policy)};
47+
\t\tct state {established, related} accept;
48+
#{parent_emit_ruleset(rules)}
49+
\t}
50+
FRAGMENT
51+
end
52+
53+
def chain_forward(rules)
54+
<<~FRAGMENT.chomp
55+
\tchain forward {
56+
\t\ttype filter hook forward priority 0;
57+
#{parent_emit_ruleset(rules)}
58+
\t}
59+
FRAGMENT
60+
end
61+
62+
def chain_output(rules, policy)
63+
<<~FRAGMENT.chomp
64+
\tchain output {
65+
\t\ttype filter hook output priority 0;
66+
\t\tpolicy #{Nftables.nftables_action(policy)};
67+
\t\tct state {established, related} accept;
68+
#{parent_emit_ruleset(rules)}
69+
\t}
70+
FRAGMENT
71+
end
72+
73+
def filename_fragment
74+
['nftables', 'nftables.conf']
75+
end
76+
end
77+
78+
# Nftables implementation of a Puffy Rule formatter.
79+
class Rule < Puffy::Formatters::Base::Rule
80+
# Returns a Nftables String representation of the provided +rule+ Puffy::Rule.
81+
def emit_rule(rule)
82+
parts = []
83+
parts << emit_what(rule)
84+
"\t\t#{parts.flatten.compact.join(' ')};"
85+
end
86+
87+
private
88+
89+
def emit_what(rule)
90+
parts = []
91+
parts << emit_proto(rule)
92+
parts += emit_from(rule)
93+
parts += emit_to(rule)
94+
parts << Nftables.nftables_action(rule)
95+
parts
96+
end
97+
98+
def emit_proto(rule)
99+
case rule.proto
100+
when :icmp
101+
%w[ip protocol icmp]
102+
else
103+
rule.proto
104+
end
105+
end
106+
107+
def emit_from(rule)
108+
parts = []
109+
parts += ['saddr', rule.from_host] if rule.from_host
110+
parts += ['sport', rule.from_port] if rule.from_port
111+
parts
112+
end
113+
114+
def emit_to(rule)
115+
parts = []
116+
parts += ['daddr', rule.to_host] if rule.to_host
117+
parts += ['dport', rule.to_port] if rule.to_port
118+
parts
119+
end
120+
end
121+
end
122+
end
123+
end

0 commit comments

Comments
 (0)