mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
596 lines
26 KiB
Go
596 lines
26 KiB
Go
/*
|
|
Copyright 2022 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package testing
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/lithammer/dedent"
|
|
|
|
"k8s.io/kubernetes/pkg/util/iptables"
|
|
utilpointer "k8s.io/utils/pointer"
|
|
)
|
|
|
|
func TestParseRule(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
rule string
|
|
parsed *Rule
|
|
nonStrict bool
|
|
err string
|
|
}{
|
|
{
|
|
name: "basic rule",
|
|
rule: `-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`,
|
|
Chain: iptables.Chain("KUBE-NODEPORTS"),
|
|
Comment: &IPTablesValue{Value: "ns2/svc2:p80 health check node port"},
|
|
Protocol: &IPTablesValue{Value: "tcp"},
|
|
DestinationPort: &IPTablesValue{Value: "30000"},
|
|
Jump: &IPTablesValue{Value: "ACCEPT"},
|
|
},
|
|
},
|
|
{
|
|
name: "addRuleToChainRegex requires an actual rule, not just a chain name",
|
|
rule: `-A KUBE-NODEPORTS`,
|
|
err: `(no match rules)`,
|
|
},
|
|
{
|
|
name: "ParseRule only parses adds",
|
|
rule: `-D KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`,
|
|
err: `(does not start with "-A CHAIN")`,
|
|
},
|
|
{
|
|
name: "unquoted comment",
|
|
rule: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
|
|
Chain: iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
|
|
Comment: &IPTablesValue{Value: "ns1/svc1:p80"},
|
|
Jump: &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
|
|
},
|
|
},
|
|
{
|
|
name: "local source",
|
|
rule: `-A KUBE-XLB-GNZBNJ2PO5MGZ6GT -m comment --comment "masquerade LOCAL traffic for ns2/svc2:p80 LB IP" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-XLB-GNZBNJ2PO5MGZ6GT -m comment --comment "masquerade LOCAL traffic for ns2/svc2:p80 LB IP" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ`,
|
|
Chain: iptables.Chain("KUBE-XLB-GNZBNJ2PO5MGZ6GT"),
|
|
Comment: &IPTablesValue{Value: "masquerade LOCAL traffic for ns2/svc2:p80 LB IP"},
|
|
SourceType: &IPTablesValue{Value: "LOCAL"},
|
|
Jump: &IPTablesValue{Value: "KUBE-MARK-MASQ"},
|
|
},
|
|
},
|
|
{
|
|
name: "not local destination",
|
|
rule: `-A RULE-TYPE-NOT-CURRENTLY-USED-BY-KUBE-PROXY -m addrtype ! --dst-type LOCAL -j KUBE-MARK-MASQ`,
|
|
parsed: &Rule{
|
|
Raw: `-A RULE-TYPE-NOT-CURRENTLY-USED-BY-KUBE-PROXY -m addrtype ! --dst-type LOCAL -j KUBE-MARK-MASQ`,
|
|
Chain: iptables.Chain("RULE-TYPE-NOT-CURRENTLY-USED-BY-KUBE-PROXY"),
|
|
DestinationType: &IPTablesValue{Negated: true, Value: "LOCAL"},
|
|
Jump: &IPTablesValue{Value: "KUBE-MARK-MASQ"},
|
|
},
|
|
},
|
|
{
|
|
name: "destination IP/port",
|
|
rule: `-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 172.30.0.41 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 172.30.0.41 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O`,
|
|
Chain: iptables.Chain("KUBE-SERVICES"),
|
|
Comment: &IPTablesValue{Value: "ns1/svc1:p80 cluster IP"},
|
|
Protocol: &IPTablesValue{Value: "tcp"},
|
|
DestinationAddress: &IPTablesValue{Value: "172.30.0.41"},
|
|
DestinationPort: &IPTablesValue{Value: "80"},
|
|
Jump: &IPTablesValue{Value: "KUBE-SVC-XPGD46QRK7WJZT7O"},
|
|
},
|
|
},
|
|
{
|
|
name: "source IP",
|
|
rule: `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ`,
|
|
Chain: iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
|
|
Comment: &IPTablesValue{Value: "ns1/svc1:p80"},
|
|
SourceAddress: &IPTablesValue{Value: "10.180.0.1"},
|
|
Jump: &IPTablesValue{Value: "KUBE-MARK-MASQ"},
|
|
},
|
|
},
|
|
{
|
|
name: "not source IP",
|
|
rule: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 172.30.0.41 --dport 80 ! -s 10.0.0.0/8 -j KUBE-MARK-MASQ`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 172.30.0.41 --dport 80 ! -s 10.0.0.0/8 -j KUBE-MARK-MASQ`,
|
|
Chain: iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
|
|
Comment: &IPTablesValue{Value: "ns1/svc1:p80 cluster IP"},
|
|
Protocol: &IPTablesValue{Value: "tcp"},
|
|
DestinationAddress: &IPTablesValue{Value: "172.30.0.41"},
|
|
DestinationPort: &IPTablesValue{Value: "80"},
|
|
SourceAddress: &IPTablesValue{Negated: true, Value: "10.0.0.0/8"},
|
|
Jump: &IPTablesValue{Value: "KUBE-MARK-MASQ"},
|
|
},
|
|
},
|
|
{
|
|
name: "affinity",
|
|
rule: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -m recent --name KUBE-SEP-SXIVWICOYRO3J4NJ --rcheck --seconds 10800 --reap -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -m recent --name KUBE-SEP-SXIVWICOYRO3J4NJ --rcheck --seconds 10800 --reap -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
|
|
Chain: iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
|
|
Comment: &IPTablesValue{Value: "ns1/svc1:p80"},
|
|
AffinityName: &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
|
|
AffinitySeconds: &IPTablesValue{Value: "10800"},
|
|
AffinityCheck: utilpointer.Bool(true),
|
|
AffinityReap: utilpointer.Bool(true),
|
|
Jump: &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
|
|
},
|
|
},
|
|
{
|
|
name: "jump to DNAT",
|
|
rule: `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80`,
|
|
Chain: iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
|
|
Comment: &IPTablesValue{Value: "ns1/svc1:p80"},
|
|
Protocol: &IPTablesValue{Value: "tcp"},
|
|
Jump: &IPTablesValue{Value: "DNAT"},
|
|
DNATDestination: &IPTablesValue{Value: "10.180.0.1:80"},
|
|
},
|
|
},
|
|
{
|
|
name: "jump to endpoint",
|
|
rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
|
|
Chain: iptables.Chain("KUBE-SVC-4SW47YFZTEDKD3PK"),
|
|
Comment: &IPTablesValue{Value: "ns4/svc4:p80"},
|
|
Probability: &IPTablesValue{Value: "0.5000000000"},
|
|
StatisticMode: &IPTablesValue{Value: "random"},
|
|
Jump: &IPTablesValue{Value: "KUBE-SEP-UKSFD7AGPMPPLUHC"},
|
|
},
|
|
},
|
|
{
|
|
name: "unrecognized arguments",
|
|
rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -i eth0 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
|
|
err: `unrecognized parameter "-i"`,
|
|
},
|
|
{
|
|
name: "unrecognized arguments with strict=false",
|
|
rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -i eth0 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
|
|
nonStrict: true,
|
|
parsed: &Rule{
|
|
Raw: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -i eth0 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
|
|
Chain: iptables.Chain("KUBE-SVC-4SW47YFZTEDKD3PK"),
|
|
Comment: &IPTablesValue{Value: "ns4/svc4:p80"},
|
|
Jump: &IPTablesValue{Value: "KUBE-SEP-UKSFD7AGPMPPLUHC"},
|
|
},
|
|
},
|
|
{
|
|
name: "bad use of !",
|
|
rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 ! -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
|
|
err: `cannot negate parameter "-j"`,
|
|
},
|
|
{
|
|
name: "missing argument",
|
|
rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -j`,
|
|
err: `parameter "-j" requires an argument`,
|
|
},
|
|
{
|
|
name: "negated bool arg",
|
|
rule: `-A TEST -m recent ! --rcheck -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
|
|
parsed: &Rule{
|
|
Raw: `-A TEST -m recent ! --rcheck -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
|
|
Chain: iptables.Chain("TEST"),
|
|
AffinityCheck: utilpointer.Bool(false),
|
|
Jump: &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
rule, err := ParseRule(testCase.rule, !testCase.nonStrict)
|
|
if err != nil {
|
|
if testCase.err == "" {
|
|
t.Errorf("expected %+v, got error %q", testCase.parsed, err)
|
|
} else if !strings.Contains(err.Error(), testCase.err) {
|
|
t.Errorf("wrong error, expected %q got %q", testCase.err, err)
|
|
}
|
|
} else {
|
|
if testCase.err != "" {
|
|
t.Errorf("expected error %q, got %+v", testCase.err, rule)
|
|
} else if !reflect.DeepEqual(rule, testCase.parsed) {
|
|
t.Errorf("bad match: expected\n%+v\ngot\n%+v", testCase.parsed, rule)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper for TestParseIPTablesDump. Obviously it should not be used in TestParseRule...
|
|
func mustParseRule(rule string) *Rule {
|
|
parsed, err := ParseRule(rule, false)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("failed to parse test case rule %q: %v", rule, err))
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func TestParseIPTablesDump(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
input string
|
|
output *IPTablesDump
|
|
error string
|
|
}{
|
|
{
|
|
name: "basic test",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
*nat
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
:KUBE-POSTROUTING - [0:0]
|
|
:KUBE-MARK-MASQ - [0:0]
|
|
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
|
|
:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
|
|
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
|
|
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
|
|
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
|
|
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
|
|
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
|
|
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ
|
|
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ
|
|
-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ
|
|
-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80
|
|
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
|
|
COMMIT
|
|
`),
|
|
output: &IPTablesDump{
|
|
Tables: []Table{{
|
|
Name: iptables.TableFilter,
|
|
Chains: []Chain{{
|
|
Name: iptables.Chain("KUBE-SERVICES"),
|
|
}, {
|
|
Name: iptables.Chain("KUBE-EXTERNAL-SERVICES"),
|
|
}, {
|
|
Name: iptables.Chain("KUBE-FORWARD"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP`),
|
|
mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT`),
|
|
mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-NODEPORTS"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`),
|
|
},
|
|
}},
|
|
}, {
|
|
Name: iptables.TableNAT,
|
|
Chains: []Chain{{
|
|
Name: iptables.Chain("KUBE-SERVICES"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O`),
|
|
mustParseRule(`-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-NODEPORTS"),
|
|
}, {
|
|
Name: iptables.Chain("KUBE-POSTROUTING"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN`),
|
|
mustParseRule(`-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000`),
|
|
mustParseRule(`-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-MARK-MASQ"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ`),
|
|
mustParseRule(`-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ`),
|
|
mustParseRule(`-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80`),
|
|
},
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "deletion",
|
|
input: dedent.Dedent(`
|
|
*nat
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
|
|
:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
|
|
-X KUBE-SVC-XPGD46QRK7WJZT7O
|
|
-X KUBE-SEP-SXIVWICOYRO3J4NJ
|
|
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
|
|
COMMIT
|
|
`),
|
|
output: &IPTablesDump{
|
|
Tables: []Table{{
|
|
Name: iptables.TableNAT,
|
|
Chains: []Chain{{
|
|
Name: iptables.Chain("KUBE-SERVICES"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
|
|
Deleted: true,
|
|
}, {
|
|
Name: iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
|
|
Deleted: true,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "whitespace and comments",
|
|
input: dedent.Dedent(`
|
|
# Generated by iptables-save v1.8.7 on Mon May 9 11:22:21 2022
|
|
# (not really...)
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
# This rule does a thing
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
# Completed on Mon May 9 11:22:21 2022
|
|
`),
|
|
output: &IPTablesDump{
|
|
Tables: []Table{{
|
|
Name: iptables.TableFilter,
|
|
Chains: []Chain{{
|
|
Name: iptables.Chain("KUBE-SERVICES"),
|
|
}, {
|
|
Name: iptables.Chain("KUBE-EXTERNAL-SERVICES"),
|
|
}, {
|
|
Name: iptables.Chain("KUBE-FORWARD"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP`),
|
|
mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT`),
|
|
mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT`),
|
|
},
|
|
}, {
|
|
Name: iptables.Chain("KUBE-NODEPORTS"),
|
|
Rules: []*Rule{
|
|
mustParseRule(`-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`),
|
|
},
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "no COMMIT line",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
`),
|
|
error: "no COMMIT line?",
|
|
},
|
|
{
|
|
name: "two tables, no second COMMIT line",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
*nat
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
:KUBE-POSTROUTING - [0:0]
|
|
:KUBE-MARK-MASQ - [0:0]
|
|
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
|
|
:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
|
|
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
|
|
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
|
|
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
|
|
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
|
|
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
|
|
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ
|
|
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ
|
|
-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ
|
|
-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80
|
|
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
|
|
`),
|
|
error: "no COMMIT line?",
|
|
},
|
|
{
|
|
name: "two tables, no second header line",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
:KUBE-POSTROUTING - [0:0]
|
|
:KUBE-MARK-MASQ - [0:0]
|
|
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
|
|
:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
|
|
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
|
|
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
|
|
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
|
|
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
|
|
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
|
|
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ
|
|
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ
|
|
-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ
|
|
-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80
|
|
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
|
|
COMMIT
|
|
`),
|
|
error: "not a table name",
|
|
},
|
|
{
|
|
name: "trailing junk",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
*nat
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
junk
|
|
`),
|
|
error: `table 3 starts with "junk"`,
|
|
},
|
|
{
|
|
name: "add to missing chain",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
`),
|
|
error: `no such chain "KUBE-FORWARD"`,
|
|
},
|
|
{
|
|
name: "add to deleted chain",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-X KUBE-FORWARD
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
`),
|
|
error: `cannot add rules to deleted chain`,
|
|
},
|
|
{
|
|
name: "deleted non-empty chain",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
-X KUBE-FORWARD
|
|
COMMIT
|
|
`),
|
|
error: `cannot delete chain "KUBE-FORWARD" after adding rules`,
|
|
},
|
|
{
|
|
name: "junk rule",
|
|
input: dedent.Dedent(`
|
|
*filter
|
|
:KUBE-SERVICES - [0:0]
|
|
:KUBE-EXTERNAL-SERVICES - [0:0]
|
|
:KUBE-FORWARD - [0:0]
|
|
:KUBE-NODEPORTS - [0:0]
|
|
-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
|
|
-Q KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
|
|
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
COMMIT
|
|
`),
|
|
error: `"-Q KUBE-FORWARD`,
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dump, err := ParseIPTablesDump(tc.input)
|
|
if err == nil {
|
|
if tc.error != "" {
|
|
t.Errorf("unexpectedly did not get error")
|
|
} else if !reflect.DeepEqual(tc.output, dump) {
|
|
t.Errorf("bad output: expected %#v got %#v", tc.output, dump)
|
|
}
|
|
} else {
|
|
if tc.error == "" {
|
|
t.Errorf("got unexpected error: %v", err)
|
|
} else if !strings.Contains(err.Error(), tc.error) {
|
|
t.Errorf("got wrong error: %v (expected %q)", err, tc.error)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|