mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	This had to be able to build on OS X before to make verify-typecheck pass, but now that that's fixed we can tag the code properly as being linux-only.
		
			
				
	
	
		
			599 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			599 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
//go:build linux
 | 
						|
// +build linux
 | 
						|
 | 
						|
/*
 | 
						|
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)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |