-
Notifications
You must be signed in to change notification settings - Fork 4.4k
/
iptables.go
195 lines (152 loc) · 8.26 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
184
185
186
187
188
189
190
191
192
193
194
195
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package iptables
import (
"errors"
"fmt"
"strconv"
)
const (
// ProxyInboundChain is the chain to intercept inbound traffic.
ProxyInboundChain = "CONSUL_PROXY_INBOUND"
// ProxyInboundRedirectChain is the chain to redirect inbound traffic to the proxy.
ProxyInboundRedirectChain = "CONSUL_PROXY_IN_REDIRECT"
// ProxyOutputChain is the chain to intercept outbound traffic.
ProxyOutputChain = "CONSUL_PROXY_OUTPUT"
// ProxyOutputRedirectChain is the chain to redirect outbound traffic to the proxy
ProxyOutputRedirectChain = "CONSUL_PROXY_REDIRECT"
// DNSChain is the chain to redirect outbound DNS traffic to Consul DNS.
DNSChain = "CONSUL_DNS_REDIRECT"
DefaultTProxyOutboundPort = 15001
)
// Config is used to configure which traffic interception and redirection
// rules should be applied with the iptables commands.
type Config struct {
// ConsulDNSIP is the IP for Consul DNS to direct DNS queries to.
ConsulDNSIP string
// ConsulDNSPort is the port for Consul DNS to direct DNS queries to.
ConsulDNSPort int
// ProxyUserID is the user ID of the proxy process.
ProxyUserID string
// ProxyInboundPort is the port of the proxy's inbound listener.
ProxyInboundPort int
// ProxyInboundPort is the port of the proxy's outbound listener.
ProxyOutboundPort int
// ExcludeInboundPorts is the list of ports that should be excluded
// from inbound traffic redirection.
ExcludeInboundPorts []string
// ExcludeOutboundPorts is the list of ports that should be excluded
// from outbound traffic redirection.
ExcludeOutboundPorts []string
// ExcludeOutboundCIDRs is the list of IP CIDRs that should be excluded
// from outbound traffic redirection.
ExcludeOutboundCIDRs []string
// ExcludeUIDs is the list of additional user IDs to exclude
// from traffic redirection.
ExcludeUIDs []string
// NetNS is the network namespace where the traffic redirection rules
// should be applied. This must be a path to the network namespace,
// e.g. /var/run/netns/foo.
NetNS string
// IptablesProvider is the Provider that will apply iptables rules.
IptablesProvider Provider
}
// Provider is an interface for executing iptables rules.
type Provider interface {
// AddRule adds a rule without executing it.
AddRule(name string, args ...string)
// ApplyRules executes rules that have been added via AddRule.
// This operation is currently not atomic, and if there's an error applying rules,
// you may be left in a state where partial rules were applied.
ApplyRules() error
// Rules returns the list of rules that have been added but not applied yet.
Rules() []string
}
// Setup will set up iptables interception and redirection rules
// based on the configuration provided in cfg.
// This implementation was inspired by
// https://github.com/openservicemesh/osm/blob/650a1a1dcf081ae90825f3b5dba6f30a0e532725/pkg/injector/iptables.go
func Setup(cfg Config) error {
if cfg.IptablesProvider == nil {
cfg.IptablesProvider = &iptablesExecutor{cfg: cfg}
}
err := validateConfig(cfg)
if err != nil {
return err
}
// Set the default outbound port if it's not already set.
if cfg.ProxyOutboundPort == 0 {
cfg.ProxyOutboundPort = DefaultTProxyOutboundPort
}
// Create chains we will use for redirection.
chains := []string{ProxyInboundChain, ProxyInboundRedirectChain, ProxyOutputChain, ProxyOutputRedirectChain, DNSChain}
for _, chain := range chains {
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-N", chain)
}
// Configure outbound rules.
{
// Redirects outbound TCP traffic hitting PROXY_REDIRECT chain to Envoy's outbound listener port.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputRedirectChain, "-p", "tcp", "-j", "REDIRECT", "--to-port", strconv.Itoa(cfg.ProxyOutboundPort))
// The DNS rules are applied before the rules that directs all TCP traffic, so that the traffic going to port 53 goes through this rule first.
if cfg.ConsulDNSIP != "" && cfg.ConsulDNSPort == 0 {
// Traffic in the DNSChain is directed to the Consul DNS Service IP.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "udp", "--dport", "53", "-j", "DNAT", "--to-destination", cfg.ConsulDNSIP)
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "tcp", "--dport", "53", "-j", "DNAT", "--to-destination", cfg.ConsulDNSIP)
// For outbound TCP and UDP traffic going to port 53 (DNS), jump to the DNSChain.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "udp", "--dport", "53", "-j", DNSChain)
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "tcp", "--dport", "53", "-j", DNSChain)
} else if cfg.ConsulDNSPort != 0 {
consulDNSIP := "127.0.0.1"
if cfg.ConsulDNSIP != "" {
consulDNSIP = cfg.ConsulDNSIP
}
consulDNSHostPort := fmt.Sprintf("%s:%d", consulDNSIP, cfg.ConsulDNSPort)
// Traffic in the DNSChain is directed to the Consul DNS Service IP.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "udp", "-d", consulDNSIP, "--dport", "53", "-j", "DNAT", "--to-destination", consulDNSHostPort)
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", DNSChain, "-p", "tcp", "-d", consulDNSIP, "--dport", "53", "-j", "DNAT", "--to-destination", consulDNSHostPort)
// For outbound TCP and UDP traffic going to port 53 (DNS), jump to the DNSChain. Only redirect traffic that's going to consul's DNS IP.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "udp", "-d", consulDNSIP, "--dport", "53", "-j", DNSChain)
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "tcp", "-d", consulDNSIP, "--dport", "53", "-j", DNSChain)
}
// For outbound TCP traffic jump from OUTPUT chain to PROXY_OUTPUT chain.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "tcp", "-j", ProxyOutputChain)
// Don't redirect proxy traffic back to itself, return it to the next chain for processing.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputChain, "-m", "owner", "--uid-owner", cfg.ProxyUserID, "-j", "RETURN")
// Skip localhost traffic, doesn't need to be routed via the proxy.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputChain, "-d", "127.0.0.1/32", "-j", "RETURN")
// Redirect remaining outbound traffic to Envoy.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyOutputChain, "-j", ProxyOutputRedirectChain)
// We are using "insert" (-I) instead of "append" (-A) so the provided rules take precedence over default ones.
for _, outboundPort := range cfg.ExcludeOutboundPorts {
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-I", ProxyOutputChain, "-p", "tcp", "--dport", outboundPort, "-j", "RETURN")
}
for _, outboundIP := range cfg.ExcludeOutboundCIDRs {
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-I", ProxyOutputChain, "-d", outboundIP, "-j", "RETURN")
}
for _, uid := range cfg.ExcludeUIDs {
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-I", ProxyOutputChain, "-m", "owner", "--uid-owner", uid, "-j", "RETURN")
}
}
// Configure inbound rules.
{
// Redirects inbound TCP traffic hitting the PROXY_IN_REDIRECT chain to Envoy's inbound listener port.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyInboundRedirectChain, "-p", "tcp", "-j", "REDIRECT", "--to-port", strconv.Itoa(cfg.ProxyInboundPort))
// For inbound traffic jump from PREROUTING chain to PROXY_INBOUND chain.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", "PREROUTING", "-p", "tcp", "-j", ProxyInboundChain)
// Redirect remaining inbound traffic to Envoy.
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-A", ProxyInboundChain, "-p", "tcp", "-j", ProxyInboundRedirectChain)
for _, inboundPort := range cfg.ExcludeInboundPorts {
cfg.IptablesProvider.AddRule("iptables", "-t", "nat", "-I", ProxyInboundChain, "-p", "tcp", "--dport", inboundPort, "-j", "RETURN")
}
}
return cfg.IptablesProvider.ApplyRules()
}
func validateConfig(cfg Config) error {
if cfg.ProxyUserID == "" {
return errors.New("ProxyUserID is required to set up traffic redirection")
}
if cfg.ProxyInboundPort == 0 {
return errors.New("ProxyInboundPort is required to set up traffic redirection")
}
return nil
}