-
Notifications
You must be signed in to change notification settings - Fork 463
/
iptables.go
183 lines (165 loc) · 5.36 KB
/
iptables.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package utils
import (
"bytes"
"fmt"
"os/exec"
"strings"
"k8s.io/klog/v2"
"github.com/coreos/go-iptables/iptables"
v1core "k8s.io/api/core/v1"
)
var hasWait bool
// IPTablesHandler interface based on the IPTables struct from github.com/coreos/go-iptables
// which allows to mock it.
type IPTablesHandler interface {
Proto() iptables.Protocol
Exists(table, chain string, rulespec ...string) (bool, error)
Insert(table, chain string, pos int, rulespec ...string) error
Append(table, chain string, rulespec ...string) error
AppendUnique(table, chain string, rulespec ...string) error
Delete(table, chain string, rulespec ...string) error
DeleteIfExists(table, chain string, rulespec ...string) error
List(table, chain string) ([]string, error)
ListWithCounters(table, chain string) ([]string, error)
ListChains(table string) ([]string, error)
ChainExists(table, chain string) (bool, error)
Stats(table, chain string) ([][]string, error)
ParseStat(stat []string) (iptables.Stat, error)
StructuredStats(table, chain string) ([]iptables.Stat, error)
NewChain(table, chain string) error
ClearChain(table, chain string) error
RenameChain(table, oldChain, newChain string) error
DeleteChain(table, chain string) error
ClearAndDeleteChain(table, chain string) error
ClearAll() error
DeleteAll() error
ChangePolicy(table, chain, target string) error
HasRandomFully() bool
GetIptablesVersion() (int, int, int)
}
//nolint:gochecknoinits // This is actually a good usage of the init() function
func init() {
path, err := exec.LookPath("iptables-restore")
if err != nil {
return
}
args := []string{"iptables-restore", "--help"}
cmd := exec.Cmd{
Path: path,
Args: args,
}
cmdOutput, err := cmd.CombinedOutput()
if err != nil {
return
}
hasWait = strings.Contains(string(cmdOutput), "wait")
}
// SaveInto calls `iptables-save` for given table and stores result in a given buffer.
func SaveInto(iptablesBinary, table string, buffer *bytes.Buffer) error {
path, err := exec.LookPath(iptablesBinary)
if err != nil {
return err
}
stderrBuffer := bytes.NewBuffer(nil)
args := []string{iptablesBinary, "-t", table}
klog.V(9).Infof("running iptables command: path=`%s` args=%+v", path, args)
cmd := exec.Cmd{
Path: path,
Args: args,
Stdout: buffer,
Stderr: stderrBuffer,
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("%v (%s)", err, stderrBuffer)
}
return nil
}
// AppendUnique ensures that rule is in chain only once in the buffer and that the occurrence is at the end of the
// buffer
func AppendUnique(buffer *bytes.Buffer, chain string, rule []string) {
// First we need to remove any previous instances of the rule that exist, so that we can be sure that our version
// is unique and appended to the very end of the buffer
rules := strings.Split(buffer.String(), "\n")
if len(rules) > 0 && rules[len(rules)-1] == "" {
rules = rules[:len(rules)-1]
}
buffer.Reset()
for _, foundRule := range rules {
if strings.Contains(foundRule, chain) && strings.Contains(foundRule, strings.Join(rule, " ")) {
continue
}
buffer.WriteString(foundRule + "\n")
}
// Now append the rule that we wanted to be unique
Append(buffer, chain, rule)
}
// Append appends rule to chain at the end of buffer
func Append(buffer *bytes.Buffer, chain string, rule []string) {
ruleStr := strings.Join(append(append([]string{"-A", chain}, rule...), "\n"), " ")
buffer.WriteString(ruleStr)
}
// IPTablesSaveRestorer interface that defines functions to save and restore tables
type IPTablesSaveRestorer interface {
SaveInto(table string, buffer *bytes.Buffer) error
Restore(table string, data []byte) error
}
// IPTablesSaveRestore struct stores shell commands to save and restore iptables state
type IPTablesSaveRestore struct {
saveCmd string
restoreCmd string
}
// NewIPTablesSaveRestore returns an IPTablesSaveRestore
// with apparopriate commands based on ipFamily (IPv4 or IPv6)
func NewIPTablesSaveRestore(ipFamily v1core.IPFamily) *IPTablesSaveRestore {
//nolint:exhaustive // we don't need exhaustive searching for IP Families
switch ipFamily {
case v1core.IPv6Protocol:
return &IPTablesSaveRestore{
saveCmd: "ip6tables-save",
restoreCmd: "ip6tables-restore",
}
case v1core.IPv4Protocol:
fallthrough
default:
return &IPTablesSaveRestore{
saveCmd: "iptables-save",
restoreCmd: "iptables-restore",
}
}
}
func (i *IPTablesSaveRestore) exec(cmdName string, args []string, data []byte, stdoutBuffer *bytes.Buffer) error {
path, err := exec.LookPath(cmdName)
if err != nil {
return err
}
stderrBuffer := bytes.NewBuffer(nil)
cmd := exec.Cmd{
Path: path,
Args: append([]string{cmdName}, args...),
Stderr: stderrBuffer,
}
if data != nil {
cmd.Stdin = bytes.NewBuffer(data)
}
if stdoutBuffer != nil {
cmd.Stdout = stdoutBuffer
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to call %s: %v (%s)", cmdName, err, stderrBuffer)
}
return nil
}
// SaveInto saves the content of iptables table into buffer
func (i *IPTablesSaveRestore) SaveInto(table string, buffer *bytes.Buffer) error {
return i.exec(i.saveCmd, []string{"-t", table}, nil, buffer)
}
// Restore updates table with the content of data
func (i *IPTablesSaveRestore) Restore(table string, data []byte) error {
var args []string
if hasWait {
args = []string{"--wait", "-T", table}
} else {
args = []string{"-T", table}
}
return i.exec(i.restoreCmd, args, data, nil)
}