Skip to content

Commit 667d080

Browse files
authored
Add option to skip subnets (#53)
Add a flag `--subnets-to-ignore` to skip subnets from redirecting to linkerd proxy The goal is to support running linkerd with dind containers Fixes linkerd/linkerd2#6758 Signed-off-by: Michael Lin <[email protected]>
1 parent 85d0bfb commit 667d080

File tree

6 files changed

+123
-0
lines changed

6 files changed

+123
-0
lines changed

cmd/root.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"fmt"
5+
"net"
56
"os/exec"
67

78
log "github.com/sirupsen/logrus"
@@ -19,6 +20,7 @@ type RootOptions struct {
1920
PortsToRedirect []int
2021
InboundPortsToIgnore []string
2122
OutboundPortsToIgnore []string
23+
SubnetsToIgnore []string
2224
SimulateOnly bool
2325
NetNs string
2426
UseWaitFlag bool
@@ -35,6 +37,7 @@ func newRootOptions() *RootOptions {
3537
PortsToRedirect: make([]int, 0),
3638
InboundPortsToIgnore: make([]string, 0),
3739
OutboundPortsToIgnore: make([]string, 0),
40+
SubnetsToIgnore: make([]string, 0),
3841
SimulateOnly: false,
3942
NetNs: "",
4043
UseWaitFlag: false,
@@ -87,6 +90,7 @@ func NewRootCmd() *cobra.Command {
8790
cmd.PersistentFlags().IntSliceVarP(&options.PortsToRedirect, "ports-to-redirect", "r", options.PortsToRedirect, "Port to redirect to proxy, if no port is specified then ALL ports are redirected")
8891
cmd.PersistentFlags().StringSliceVar(&options.InboundPortsToIgnore, "inbound-ports-to-ignore", options.InboundPortsToIgnore, "Inbound ports and/or port ranges (inclusive) to ignore and not redirect to proxy. This has higher precedence than any other parameters.")
8992
cmd.PersistentFlags().StringSliceVar(&options.OutboundPortsToIgnore, "outbound-ports-to-ignore", options.OutboundPortsToIgnore, "Outbound ports and/or port ranges (inclusive) to ignore and not redirect to proxy. This has higher precedence than any other parameters.")
93+
cmd.PersistentFlags().StringSliceVar(&options.SubnetsToIgnore, "subnets-to-ignore", options.SubnetsToIgnore, "Subnets to ignore and not redirect to proxy. This has higher precedence than any other parameters.")
9094
cmd.PersistentFlags().BoolVar(&options.SimulateOnly, "simulate", options.SimulateOnly, "Don't execute any command, just print what would be executed")
9195
cmd.PersistentFlags().StringVar(&options.NetNs, "netns", options.NetNs, "Optional network namespace in which to run the iptables commands")
9296
cmd.PersistentFlags().BoolVarP(&options.UseWaitFlag, "use-wait-flag", "w", options.UseWaitFlag, "Appends the \"-w\" flag to the iptables commands")
@@ -106,13 +110,21 @@ func BuildFirewallConfiguration(options *RootOptions) (*iptables.FirewallConfigu
106110
return nil, fmt.Errorf("--outgoing-proxy-port must be a valid TCP port number")
107111
}
108112

113+
for _, subnet := range options.SubnetsToIgnore {
114+
_, _, err := net.ParseCIDR(subnet)
115+
if err != nil {
116+
return nil, fmt.Errorf("%s is not a valid CIDR address", subnet)
117+
}
118+
}
119+
109120
firewallConfiguration := &iptables.FirewallConfiguration{
110121
ProxyInboundPort: options.IncomingProxyPort,
111122
ProxyOutgoingPort: options.OutgoingProxyPort,
112123
ProxyUID: options.ProxyUserID,
113124
PortsToRedirectInbound: options.PortsToRedirect,
114125
InboundPortsToIgnore: options.InboundPortsToIgnore,
115126
OutboundPortsToIgnore: options.OutboundPortsToIgnore,
127+
SubnetsToIgnore: options.SubnetsToIgnore,
116128
SimulateOnly: options.SimulateOnly,
117129
NetNs: options.NetNs,
118130
UseWaitFlag: options.UseWaitFlag,

cmd/root_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func TestBuildFirewallConfiguration(t *testing.T) {
1717
PortsToRedirectInbound: make([]int, 0),
1818
InboundPortsToIgnore: make([]string, 0),
1919
OutboundPortsToIgnore: make([]string, 0),
20+
SubnetsToIgnore: make([]string, 0),
2021
ProxyInboundPort: expectedIncomingProxyPort,
2122
ProxyOutgoingPort: expectedOutgoingProxyPort,
2223
ProxyUID: expectedProxyUserID,
@@ -72,6 +73,12 @@ func TestBuildFirewallConfiguration(t *testing.T) {
7273
},
7374
errorMessage: "--outgoing-proxy-port must be a valid TCP port number",
7475
},
76+
{
77+
options: &RootOptions{
78+
SubnetsToIgnore: []string{"1.1.1.1/24", "0.0.0.0"},
79+
},
80+
errorMessage: "0.0.0.0 is not a valid CIDR address",
81+
},
7582
} {
7683
_, err := BuildFirewallConfiguration(tt.options)
7784
if err == nil {

integration_test/iptables/http_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,30 @@ func TestPodMakesOutboundConnection(t *testing.T) {
177177
})
178178
}
179179

180+
func TestPodWithSomeSubnetsIgnored(t *testing.T) {
181+
t.Parallel()
182+
183+
podIgnoredSomeSubnetsIP := os.Getenv("POD_IGNORES_SUBNETS_IP")
184+
185+
t.Run("connecting to a not-a-proxy-container should bypass proxy container", func(t *testing.T) {
186+
response := expectSuccessfulGetRequestTo(t, podIgnoredSomeSubnetsIP, notTheProxyContainerPort)
187+
188+
expectedResponse := fmt.Sprintf("pod-ignores-subnets:%s", notTheProxyContainerPort)
189+
if !strings.Contains(response, expectedResponse) {
190+
t.Fatalf("Expected response to be bypassed, expected %s but it was %s", expectedResponse, response)
191+
}
192+
})
193+
194+
t.Run("connecting directly to the proxy container pod should still work", func(t *testing.T) {
195+
response := expectSuccessfulGetRequestTo(t, podIgnoredSomeSubnetsIP, proxyContainerPort)
196+
197+
expectedResponse := "proxy"
198+
if !strings.Contains(response, expectedResponse) {
199+
t.Fatalf("Expected response from the proxy container, expected %s but it was %s", expectedResponse, response)
200+
}
201+
})
202+
}
203+
180204
func makeCallFromContainerToAnother(t *testing.T, fromPodNamed string, fromContainerAtPort string, podIWantToReachName string, containerPortIWantToReach string) string {
181205
downstreamURL := fmt.Sprintf("http://%s:%s", podIWantToReachName, containerPortIWantToReach)
182206

integration_test/iptables/iptablestest-lab.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,56 @@ spec:
274274
volumes:
275275
- emptyDir: {}
276276
name: linkerd-proxy-init-xtables-lock
277+
---
278+
apiVersion: v1
279+
kind: Pod
280+
metadata:
281+
name: pod-ignores-subnets
282+
labels:
283+
app: pod-ignores-subnets
284+
spec:
285+
containers:
286+
- name: proxy-stub
287+
image: ghcr.io/linkerd/iptables-tester:v1
288+
env:
289+
- name: PORT
290+
value: "8080"
291+
- name: AM_I_THE_PROXY
292+
value: "yes"
293+
command: ["go", "run", "/go/test_service/test_service.go"]
294+
ports:
295+
- name: http
296+
containerPort: 8080
297+
securityContext:
298+
privileged: false
299+
runAsUser: 2102
300+
- name: other-container
301+
image: ghcr.io/linkerd/iptables-tester:v1
302+
env:
303+
- name: PORT
304+
value: "9090"
305+
command: ["go", "run", "/go/test_service/test_service.go"]
306+
ports:
307+
- name: http
308+
containerPort: 9090
309+
initContainers:
310+
- name: linkerd-init
311+
image: ghcr.io/linkerd/proxy-init:latest
312+
imagePullPolicy: Never
313+
args: ["-p", "8080", "-o", "8080", "-u", "2102", "--subnets-to-ignore", "0.0.0.0/0"]
314+
securityContext:
315+
allowPrivilegeEscalation: false
316+
capabilities:
317+
add:
318+
- NET_ADMIN
319+
- NET_RAW
320+
privileged: false
321+
readOnlyRootFilesystem: true
322+
runAsNonRoot: false
323+
runAsUser: 0
324+
volumeMounts:
325+
- mountPath: /run
326+
name: linkerd-proxy-init-xtables-lock
327+
volumes:
328+
- emptyDir: {}
329+
name: linkerd-proxy-init-xtables-lock

integration_test/run_tests.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ log "POD_REDIRECTS_WHITELISTED_IP=${POD_REDIRECTS_WHITELISTED_IP}"
6060
POD_DOEST_REDIRECT_BLACKLISTED_IP=$(get_ip_for_pod 'pod-doesnt-redirect-blacklisted')
6161
log "POD_DOEST_REDIRECT_BLACKLISTED_IP=${POD_DOEST_REDIRECT_BLACKLISTED_IP}"
6262

63+
POD_IGNORES_SUBNETS_IP=$(get_ip_for_pod 'pod-ignores-subnets')
64+
log "POD_IGNORES_SUBNETS_IP=${POD_IGNORES_SUBNETS_IP}"
65+
6366
header 'Running tester...'
6467
cat <<EOF | kubectl create -f -
6568
apiVersion: batch/v1
@@ -85,6 +88,8 @@ spec:
8588
value: ${POD_REDIRECTS_WHITELISTED_IP}
8689
- name: POD_DOEST_REDIRECT_BLACKLISTED_IP
8790
value: ${POD_DOEST_REDIRECT_BLACKLISTED_IP}
91+
- name: POD_IGNORES_SUBNETS_IP
92+
value: ${POD_IGNORES_SUBNETS_IP}
8893
restartPolicy: Never
8994
EOF
9095

iptables/iptables.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type FirewallConfiguration struct {
4848
PortsToRedirectInbound []int
4949
InboundPortsToIgnore []string
5050
OutboundPortsToIgnore []string
51+
SubnetsToIgnore []string
5152
ProxyInboundPort int
5253
ProxyOutgoingPort int
5354
ProxyUID int
@@ -133,6 +134,7 @@ func addOutgoingTrafficRules(commands []*exec.Cmd, firewallConfiguration Firewal
133134
func addIncomingTrafficRules(commands []*exec.Cmd, firewallConfiguration FirewallConfiguration) []*exec.Cmd {
134135
commands = append(commands, makeCreateNewChain(redirectChainName, "redirect-common-chain"))
135136
commands = addRulesForIgnoredPorts(firewallConfiguration.InboundPortsToIgnore, redirectChainName, commands)
137+
commands = addRulesForIgnoredSubnets(firewallConfiguration.SubnetsToIgnore, redirectChainName, commands)
136138
commands = addRulesForInboundPortRedirect(firewallConfiguration, redirectChainName, commands)
137139

138140
// Redirect all remaining inbound traffic to the proxy.
@@ -179,6 +181,15 @@ func addRulesForIgnoredPorts(portsToIgnore []string, chainName string, commands
179181
return commands
180182
}
181183

184+
func addRulesForIgnoredSubnets(subnetsToIgnore []string, chainName string, commands []*exec.Cmd) []*exec.Cmd {
185+
for _, subnet := range subnetsToIgnore {
186+
log.Infof("Will ignore subnet %s on chain %s", subnet, chainName)
187+
188+
commands = append(commands, makeIgnoreSubnet(chainName, subnet, fmt.Sprintf("ignore-subnet-%s", subnet)))
189+
}
190+
return commands
191+
}
192+
182193
func makeMultiportDestinations(portsToIgnore []string) [][]string {
183194
destinationSlices := make([][]string, 0)
184195
destinationPortCount := 0
@@ -297,6 +308,17 @@ func makeIgnorePorts(chainName string, destinations []string, comment string) *e
297308
"--comment", formatComment(comment))
298309
}
299310

311+
func makeIgnoreSubnet(chainName string, subnet string, comment string) *exec.Cmd {
312+
return exec.Command("iptables",
313+
"-t", "nat",
314+
"-A", chainName,
315+
"-p", "all",
316+
"-j", "RETURN",
317+
"-s", subnet,
318+
"-m", "comment",
319+
"--comment", formatComment(comment))
320+
}
321+
300322
func makeIgnoreLoopback(chainName string, comment string) *exec.Cmd {
301323
return exec.Command("iptables",
302324
"-t", "nat",

0 commit comments

Comments
 (0)