/
iptables.go
149 lines (122 loc) · 4.81 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
package iptables
import (
"bytes"
"fmt"
"os/exec"
"strings"
"code.cloudfoundry.org/guardian/pkg/locksmith"
"code.cloudfoundry.org/commandrunner"
)
const LockKey = "/var/run/garden-iptables.lock"
type Locksmith interface {
Lock(key string) (locksmith.Unlocker, error)
}
//counterfeiter:generate . Rule
type Rule interface {
Flags(chain string) []string
}
//counterfeiter:generate . IPTables
type IPTables interface {
CreateChain(table, chain string) error
DeleteChain(table, chain string) error
FlushChain(table, chain string) error
DeleteChainReferences(table, targetChain, referencedChain string) error
PrependRule(chain string, rule Rule) error
BulkPrependRules(chain string, rules []Rule) error
InstanceChain(instanceId string) string
}
type IPTablesController struct {
runner commandrunner.CommandRunner
locksmith Locksmith
iptablesBinPath string
iptablesRestoreBinPath string
preroutingChain, postroutingChain, inputChain, forwardChain, defaultChain, instanceChainPrefix string
}
type Chains struct {
Prerouting, Postrouting, Input, Forward, Default string
}
func New(iptablesBinPath, iptablesRestoreBinPath string, runner commandrunner.CommandRunner, locksmith Locksmith, chainPrefix string) *IPTablesController {
return &IPTablesController{
runner: runner,
locksmith: locksmith,
iptablesBinPath: iptablesBinPath,
iptablesRestoreBinPath: iptablesRestoreBinPath,
preroutingChain: chainPrefix + "prerouting",
postroutingChain: chainPrefix + "postrouting",
inputChain: chainPrefix + "input",
forwardChain: chainPrefix + "forward",
defaultChain: chainPrefix + "default",
instanceChainPrefix: chainPrefix + "instance-",
}
}
func (iptables *IPTablesController) CreateChain(table, chain string) error {
return iptables.run("create-instance-chains", exec.Command(iptables.iptablesBinPath, "--wait", "--table", table, "-N", chain))
}
func (iptables *IPTablesController) DeleteChain(table, chain string) error {
shellCmd := fmt.Sprintf(
`%s --wait --table %s -X %s 2> /dev/null || true`,
iptables.iptablesBinPath, table, chain,
)
return iptables.run("delete-instance-chains", exec.Command("sh", "-c", shellCmd))
}
func (iptables *IPTablesController) FlushChain(table, chain string) error {
shellCmd := fmt.Sprintf(
`%s --wait --table %s -F %s 2> /dev/null || true`,
iptables.iptablesBinPath, table, chain,
)
return iptables.run("flush-instance-chains", exec.Command("sh", "-c", shellCmd))
}
func (iptables *IPTablesController) DeleteChainReferences(table, targetChain, referencedChain string) error {
shellCmd := fmt.Sprintf(
`set -e; %s --wait --table %s -S %s | grep "%s" | sed -e "s/-A/-D/" | xargs --no-run-if-empty --max-lines=1 %s -w -t %s`,
iptables.iptablesBinPath, table, targetChain, referencedChain, iptables.iptablesBinPath, table,
)
return iptables.run("delete-referenced-chains", exec.Command("sh", "-c", shellCmd))
}
func (iptables *IPTablesController) PrependRule(chain string, rule Rule) error {
return iptables.run("prepend-rule", exec.Command(iptables.iptablesBinPath, append([]string{"-w", "-I", chain, "1"}, rule.Flags(chain)...)...))
}
func (iptables *IPTablesController) BulkPrependRules(chain string, rules []Rule) error {
if len(rules) == 0 {
return nil
}
in := bytes.NewBuffer([]byte{})
in.WriteString("*filter\n")
for _, r := range rules {
in.WriteString(fmt.Sprintf("-I %s 1 ", chain))
in.WriteString(strings.Join(r.Flags(chain), " "))
in.WriteString("\n")
}
in.WriteString("COMMIT\n")
cmd := exec.Command(iptables.iptablesRestoreBinPath, "--noflush")
cmd.Stdin = in
return iptables.run("bulk-prepend-rules", cmd)
}
func (iptables *IPTablesController) InstanceChain(instanceId string) string {
return iptables.instanceChainPrefix + instanceId
}
func (iptables *IPTablesController) run(action string, cmd *exec.Cmd) (err error) {
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
u, err := iptables.locksmith.Lock(LockKey)
if err != nil {
return err
}
defer func() {
if unlockErr := u.Unlock(); unlockErr != nil {
if err != nil {
err = fmt.Errorf("%s and then %s", err, unlockErr)
} else {
err = unlockErr
}
}
}()
if err := iptables.runner.Run(cmd); err != nil {
return fmt.Errorf("iptables: %s: %s", action, buff.String())
}
return nil
}
func (iptables *IPTablesController) appendRule(chain string, rule Rule) error {
return iptables.run("append-rule", exec.Command(iptables.iptablesBinPath, append([]string{"-w", "-A", chain}, rule.Flags(chain)...)...))
}