...

Source file src/k8s.io/kubernetes/pkg/util/iptables/testing/parse_test.go

Documentation: k8s.io/kubernetes/pkg/util/iptables/testing

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2022 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package testing
    21  
    22  import (
    23  	"fmt"
    24  	"reflect"
    25  	"strings"
    26  	"testing"
    27  
    28  	"github.com/lithammer/dedent"
    29  
    30  	"k8s.io/kubernetes/pkg/util/iptables"
    31  	utilpointer "k8s.io/utils/pointer"
    32  )
    33  
    34  func TestParseRule(t *testing.T) {
    35  	testCases := []struct {
    36  		name      string
    37  		rule      string
    38  		parsed    *Rule
    39  		nonStrict bool
    40  		err       string
    41  	}{
    42  		{
    43  			name: "basic rule",
    44  			rule: `-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`,
    45  			parsed: &Rule{
    46  				Raw:             `-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`,
    47  				Chain:           iptables.Chain("KUBE-NODEPORTS"),
    48  				Comment:         &IPTablesValue{Value: "ns2/svc2:p80 health check node port"},
    49  				Protocol:        &IPTablesValue{Value: "tcp"},
    50  				DestinationPort: &IPTablesValue{Value: "30000"},
    51  				Jump:            &IPTablesValue{Value: "ACCEPT"},
    52  			},
    53  		},
    54  		{
    55  			name: "addRuleToChainRegex requires an actual rule, not just a chain name",
    56  			rule: `-A KUBE-NODEPORTS`,
    57  			err:  `(no match rules)`,
    58  		},
    59  		{
    60  			name: "ParseRule only parses adds",
    61  			rule: `-D KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`,
    62  			err:  `(does not start with "-A CHAIN")`,
    63  		},
    64  		{
    65  			name: "unquoted comment",
    66  			rule: `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
    67  			parsed: &Rule{
    68  				Raw:     `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
    69  				Chain:   iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
    70  				Comment: &IPTablesValue{Value: "ns1/svc1:p80"},
    71  				Jump:    &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
    72  			},
    73  		},
    74  		{
    75  			name: "local source",
    76  			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`,
    77  			parsed: &Rule{
    78  				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`,
    79  				Chain:      iptables.Chain("KUBE-XLB-GNZBNJ2PO5MGZ6GT"),
    80  				Comment:    &IPTablesValue{Value: "masquerade LOCAL traffic for ns2/svc2:p80 LB IP"},
    81  				SourceType: &IPTablesValue{Value: "LOCAL"},
    82  				Jump:       &IPTablesValue{Value: "KUBE-MARK-MASQ"},
    83  			},
    84  		},
    85  		{
    86  			name: "not local destination",
    87  			rule: `-A RULE-TYPE-NOT-CURRENTLY-USED-BY-KUBE-PROXY -m addrtype ! --dst-type LOCAL -j KUBE-MARK-MASQ`,
    88  			parsed: &Rule{
    89  				Raw:             `-A RULE-TYPE-NOT-CURRENTLY-USED-BY-KUBE-PROXY -m addrtype ! --dst-type LOCAL -j KUBE-MARK-MASQ`,
    90  				Chain:           iptables.Chain("RULE-TYPE-NOT-CURRENTLY-USED-BY-KUBE-PROXY"),
    91  				DestinationType: &IPTablesValue{Negated: true, Value: "LOCAL"},
    92  				Jump:            &IPTablesValue{Value: "KUBE-MARK-MASQ"},
    93  			},
    94  		},
    95  		{
    96  			name: "destination IP/port",
    97  			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`,
    98  			parsed: &Rule{
    99  				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`,
   100  				Chain:              iptables.Chain("KUBE-SERVICES"),
   101  				Comment:            &IPTablesValue{Value: "ns1/svc1:p80 cluster IP"},
   102  				Protocol:           &IPTablesValue{Value: "tcp"},
   103  				DestinationAddress: &IPTablesValue{Value: "172.30.0.41"},
   104  				DestinationPort:    &IPTablesValue{Value: "80"},
   105  				Jump:               &IPTablesValue{Value: "KUBE-SVC-XPGD46QRK7WJZT7O"},
   106  			},
   107  		},
   108  		{
   109  			name: "source IP",
   110  			rule: `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ`,
   111  			parsed: &Rule{
   112  				Raw:           `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ`,
   113  				Chain:         iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
   114  				Comment:       &IPTablesValue{Value: "ns1/svc1:p80"},
   115  				SourceAddress: &IPTablesValue{Value: "10.180.0.1"},
   116  				Jump:          &IPTablesValue{Value: "KUBE-MARK-MASQ"},
   117  			},
   118  		},
   119  		{
   120  			name: "not source IP",
   121  			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`,
   122  			parsed: &Rule{
   123  				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`,
   124  				Chain:              iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
   125  				Comment:            &IPTablesValue{Value: "ns1/svc1:p80 cluster IP"},
   126  				Protocol:           &IPTablesValue{Value: "tcp"},
   127  				DestinationAddress: &IPTablesValue{Value: "172.30.0.41"},
   128  				DestinationPort:    &IPTablesValue{Value: "80"},
   129  				SourceAddress:      &IPTablesValue{Negated: true, Value: "10.0.0.0/8"},
   130  				Jump:               &IPTablesValue{Value: "KUBE-MARK-MASQ"},
   131  			},
   132  		},
   133  		{
   134  			name: "affinity",
   135  			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`,
   136  			parsed: &Rule{
   137  				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`,
   138  				Chain:           iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
   139  				Comment:         &IPTablesValue{Value: "ns1/svc1:p80"},
   140  				AffinityName:    &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
   141  				AffinitySeconds: &IPTablesValue{Value: "10800"},
   142  				AffinityCheck:   utilpointer.Bool(true),
   143  				AffinityReap:    utilpointer.Bool(true),
   144  				Jump:            &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
   145  			},
   146  		},
   147  		{
   148  			name: "jump to DNAT",
   149  			rule: `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80`,
   150  			parsed: &Rule{
   151  				Raw:             `-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80`,
   152  				Chain:           iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
   153  				Comment:         &IPTablesValue{Value: "ns1/svc1:p80"},
   154  				Protocol:        &IPTablesValue{Value: "tcp"},
   155  				Jump:            &IPTablesValue{Value: "DNAT"},
   156  				DNATDestination: &IPTablesValue{Value: "10.180.0.1:80"},
   157  			},
   158  		},
   159  		{
   160  			name: "jump to endpoint",
   161  			rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
   162  			parsed: &Rule{
   163  				Raw:           `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
   164  				Chain:         iptables.Chain("KUBE-SVC-4SW47YFZTEDKD3PK"),
   165  				Comment:       &IPTablesValue{Value: "ns4/svc4:p80"},
   166  				Probability:   &IPTablesValue{Value: "0.5000000000"},
   167  				StatisticMode: &IPTablesValue{Value: "random"},
   168  				Jump:          &IPTablesValue{Value: "KUBE-SEP-UKSFD7AGPMPPLUHC"},
   169  			},
   170  		},
   171  		{
   172  			name: "unrecognized arguments",
   173  			rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -i eth0 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
   174  			err:  `unrecognized parameter "-i"`,
   175  		},
   176  		{
   177  			name:      "unrecognized arguments with strict=false",
   178  			rule:      `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -i eth0 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
   179  			nonStrict: true,
   180  			parsed: &Rule{
   181  				Raw:     `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -i eth0 -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
   182  				Chain:   iptables.Chain("KUBE-SVC-4SW47YFZTEDKD3PK"),
   183  				Comment: &IPTablesValue{Value: "ns4/svc4:p80"},
   184  				Jump:    &IPTablesValue{Value: "KUBE-SEP-UKSFD7AGPMPPLUHC"},
   185  			},
   186  		},
   187  		{
   188  			name: "bad use of !",
   189  			rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 ! -j KUBE-SEP-UKSFD7AGPMPPLUHC`,
   190  			err:  `cannot negate parameter "-j"`,
   191  		},
   192  		{
   193  			name: "missing argument",
   194  			rule: `-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -j`,
   195  			err:  `parameter "-j" requires an argument`,
   196  		},
   197  		{
   198  			name: "negated bool arg",
   199  			rule: `-A TEST -m recent ! --rcheck -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
   200  			parsed: &Rule{
   201  				Raw:           `-A TEST -m recent ! --rcheck -j KUBE-SEP-SXIVWICOYRO3J4NJ`,
   202  				Chain:         iptables.Chain("TEST"),
   203  				AffinityCheck: utilpointer.Bool(false),
   204  				Jump:          &IPTablesValue{Value: "KUBE-SEP-SXIVWICOYRO3J4NJ"},
   205  			},
   206  		},
   207  	}
   208  
   209  	for _, testCase := range testCases {
   210  		t.Run(testCase.name, func(t *testing.T) {
   211  			rule, err := ParseRule(testCase.rule, !testCase.nonStrict)
   212  			if err != nil {
   213  				if testCase.err == "" {
   214  					t.Errorf("expected %+v, got error %q", testCase.parsed, err)
   215  				} else if !strings.Contains(err.Error(), testCase.err) {
   216  					t.Errorf("wrong error, expected %q got %q", testCase.err, err)
   217  				}
   218  			} else {
   219  				if testCase.err != "" {
   220  					t.Errorf("expected error %q, got %+v", testCase.err, rule)
   221  				} else if !reflect.DeepEqual(rule, testCase.parsed) {
   222  					t.Errorf("bad match: expected\n%+v\ngot\n%+v", testCase.parsed, rule)
   223  				}
   224  			}
   225  		})
   226  	}
   227  }
   228  
   229  // Helper for TestParseIPTablesDump. Obviously it should not be used in TestParseRule...
   230  func mustParseRule(rule string) *Rule {
   231  	parsed, err := ParseRule(rule, false)
   232  	if err != nil {
   233  		panic(fmt.Sprintf("failed to parse test case rule %q: %v", rule, err))
   234  	}
   235  	return parsed
   236  }
   237  
   238  func TestParseIPTablesDump(t *testing.T) {
   239  	for _, tc := range []struct {
   240  		name   string
   241  		input  string
   242  		output *IPTablesDump
   243  		error  string
   244  	}{
   245  		{
   246  			name: "basic test",
   247  			input: dedent.Dedent(`
   248  				*filter
   249  				:KUBE-SERVICES - [0:0]
   250  				:KUBE-EXTERNAL-SERVICES - [0:0]
   251  				:KUBE-FORWARD - [0:0]
   252  				:KUBE-NODEPORTS - [0:0]
   253  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   254  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   255  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   256  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   257  				COMMIT
   258  				*nat
   259  				:KUBE-SERVICES - [0:0]
   260  				:KUBE-NODEPORTS - [0:0]
   261  				:KUBE-POSTROUTING - [0:0]
   262  				:KUBE-MARK-MASQ - [0:0]
   263  				:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
   264  				:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
   265  				-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
   266  				-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
   267  				-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
   268  				-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
   269  				-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
   270  				-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
   271  				-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ
   272  				-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ
   273  				-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80
   274  				-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
   275  				COMMIT
   276  				`),
   277  			output: &IPTablesDump{
   278  				Tables: []Table{{
   279  					Name: iptables.TableFilter,
   280  					Chains: []Chain{{
   281  						Name: iptables.Chain("KUBE-SERVICES"),
   282  					}, {
   283  						Name: iptables.Chain("KUBE-EXTERNAL-SERVICES"),
   284  					}, {
   285  						Name: iptables.Chain("KUBE-FORWARD"),
   286  						Rules: []*Rule{
   287  							mustParseRule(`-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP`),
   288  							mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT`),
   289  							mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT`),
   290  						},
   291  					}, {
   292  						Name: iptables.Chain("KUBE-NODEPORTS"),
   293  						Rules: []*Rule{
   294  							mustParseRule(`-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`),
   295  						},
   296  					}},
   297  				}, {
   298  					Name: iptables.TableNAT,
   299  					Chains: []Chain{{
   300  						Name: iptables.Chain("KUBE-SERVICES"),
   301  						Rules: []*Rule{
   302  							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`),
   303  							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`),
   304  						},
   305  					}, {
   306  						Name: iptables.Chain("KUBE-NODEPORTS"),
   307  					}, {
   308  						Name: iptables.Chain("KUBE-POSTROUTING"),
   309  						Rules: []*Rule{
   310  							mustParseRule(`-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN`),
   311  							mustParseRule(`-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000`),
   312  							mustParseRule(`-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE`),
   313  						},
   314  					}, {
   315  						Name: iptables.Chain("KUBE-MARK-MASQ"),
   316  						Rules: []*Rule{
   317  							mustParseRule(`-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000`),
   318  						},
   319  					}, {
   320  						Name: iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
   321  						Rules: []*Rule{
   322  							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`),
   323  							mustParseRule(`-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ`),
   324  						},
   325  					}, {
   326  						Name: iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
   327  						Rules: []*Rule{
   328  							mustParseRule(`-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ`),
   329  							mustParseRule(`-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80`),
   330  						},
   331  					}},
   332  				}},
   333  			},
   334  		},
   335  		{
   336  			name: "deletion",
   337  			input: dedent.Dedent(`
   338  				*nat
   339  				:KUBE-SERVICES - [0:0]
   340  				:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
   341  				:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
   342  				-X KUBE-SVC-XPGD46QRK7WJZT7O
   343  				-X KUBE-SEP-SXIVWICOYRO3J4NJ
   344  				-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
   345  				COMMIT
   346  				`),
   347  			output: &IPTablesDump{
   348  				Tables: []Table{{
   349  					Name: iptables.TableNAT,
   350  					Chains: []Chain{{
   351  						Name: iptables.Chain("KUBE-SERVICES"),
   352  						Rules: []*Rule{
   353  							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`),
   354  						},
   355  					}, {
   356  						Name:    iptables.Chain("KUBE-SVC-XPGD46QRK7WJZT7O"),
   357  						Deleted: true,
   358  					}, {
   359  						Name:    iptables.Chain("KUBE-SEP-SXIVWICOYRO3J4NJ"),
   360  						Deleted: true,
   361  					}},
   362  				}},
   363  			},
   364  		},
   365  		{
   366  			name: "whitespace and comments",
   367  			input: dedent.Dedent(`
   368  				# Generated by iptables-save v1.8.7 on Mon May  9 11:22:21 2022
   369  				# (not really...)
   370  				*filter
   371  				:KUBE-SERVICES - [0:0]
   372  				:KUBE-EXTERNAL-SERVICES - [0:0]
   373  
   374  				:KUBE-FORWARD - [0:0]
   375  				:KUBE-NODEPORTS - [0:0]
   376  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   377  				  -A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   378  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   379  				# This rule does a thing
   380  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   381  				COMMIT
   382  				# Completed on Mon May  9 11:22:21 2022
   383  				`),
   384  			output: &IPTablesDump{
   385  				Tables: []Table{{
   386  					Name: iptables.TableFilter,
   387  					Chains: []Chain{{
   388  						Name: iptables.Chain("KUBE-SERVICES"),
   389  					}, {
   390  						Name: iptables.Chain("KUBE-EXTERNAL-SERVICES"),
   391  					}, {
   392  						Name: iptables.Chain("KUBE-FORWARD"),
   393  						Rules: []*Rule{
   394  							mustParseRule(`-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP`),
   395  							mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT`),
   396  							mustParseRule(`-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT`),
   397  						},
   398  					}, {
   399  						Name: iptables.Chain("KUBE-NODEPORTS"),
   400  						Rules: []*Rule{
   401  							mustParseRule(`-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT`),
   402  						},
   403  					}},
   404  				}},
   405  			},
   406  		},
   407  		{
   408  			name: "no COMMIT line",
   409  			input: dedent.Dedent(`
   410  				*filter
   411  				:KUBE-SERVICES - [0:0]
   412  				:KUBE-EXTERNAL-SERVICES - [0:0]
   413  				:KUBE-FORWARD - [0:0]
   414  				:KUBE-NODEPORTS - [0:0]
   415  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   416  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   417  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   418  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   419  				`),
   420  			error: "no COMMIT line?",
   421  		},
   422  		{
   423  			name: "two tables, no second COMMIT line",
   424  			input: dedent.Dedent(`
   425  				*filter
   426  				:KUBE-SERVICES - [0:0]
   427  				:KUBE-EXTERNAL-SERVICES - [0:0]
   428  				:KUBE-FORWARD - [0:0]
   429  				:KUBE-NODEPORTS - [0:0]
   430  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   431  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   432  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   433  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   434  				COMMIT
   435  				*nat
   436  				:KUBE-SERVICES - [0:0]
   437  				:KUBE-NODEPORTS - [0:0]
   438  				:KUBE-POSTROUTING - [0:0]
   439  				:KUBE-MARK-MASQ - [0:0]
   440  				:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
   441  				:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
   442  				-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
   443  				-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
   444  				-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
   445  				-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
   446  				-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
   447  				-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
   448  				-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ
   449  				-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ
   450  				-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80
   451  				-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
   452  				`),
   453  			error: "no COMMIT line?",
   454  		},
   455  		{
   456  			name: "two tables, no second header line",
   457  			input: dedent.Dedent(`
   458  				*filter
   459  				:KUBE-SERVICES - [0:0]
   460  				:KUBE-EXTERNAL-SERVICES - [0:0]
   461  				:KUBE-FORWARD - [0:0]
   462  				:KUBE-NODEPORTS - [0:0]
   463  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   464  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   465  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   466  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   467  				COMMIT
   468  				:KUBE-SERVICES - [0:0]
   469  				:KUBE-NODEPORTS - [0:0]
   470  				:KUBE-POSTROUTING - [0:0]
   471  				:KUBE-MARK-MASQ - [0:0]
   472  				:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
   473  				:KUBE-SEP-SXIVWICOYRO3J4NJ - [0:0]
   474  				-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
   475  				-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
   476  				-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
   477  				-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
   478  				-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
   479  				-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
   480  				-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ
   481  				-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1 -j KUBE-MARK-MASQ
   482  				-A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80
   483  				-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
   484  				COMMIT
   485  				`),
   486  			error: "not a table name",
   487  		},
   488  		{
   489  			name: "trailing junk",
   490  			input: dedent.Dedent(`
   491  				*filter
   492  				:KUBE-SERVICES - [0:0]
   493  				:KUBE-EXTERNAL-SERVICES - [0:0]
   494  				:KUBE-FORWARD - [0:0]
   495  				:KUBE-NODEPORTS - [0:0]
   496  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   497  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   498  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   499  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   500  				COMMIT
   501  				*nat
   502  				:KUBE-SERVICES - [0:0]
   503  				:KUBE-EXTERNAL-SERVICES - [0:0]
   504  				:KUBE-FORWARD - [0:0]
   505  				:KUBE-NODEPORTS - [0:0]
   506  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   507  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   508  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   509  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   510  				COMMIT
   511  				junk
   512  				`),
   513  			error: `table 3 starts with "junk"`,
   514  		},
   515  		{
   516  			name: "add to missing chain",
   517  			input: dedent.Dedent(`
   518  				*filter
   519  				:KUBE-SERVICES - [0:0]
   520  				:KUBE-EXTERNAL-SERVICES - [0:0]
   521  				:KUBE-NODEPORTS - [0:0]
   522  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   523  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   524  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   525  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   526  				COMMIT
   527  				`),
   528  			error: `no such chain "KUBE-FORWARD"`,
   529  		},
   530  		{
   531  			name: "add to deleted chain",
   532  			input: dedent.Dedent(`
   533  				*filter
   534  				:KUBE-SERVICES - [0:0]
   535  				:KUBE-EXTERNAL-SERVICES - [0:0]
   536  				:KUBE-FORWARD - [0:0]
   537  				:KUBE-NODEPORTS - [0:0]
   538  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   539  				-X KUBE-FORWARD
   540  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   541  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   542  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   543  				COMMIT
   544  				`),
   545  			error: `cannot add rules to deleted chain`,
   546  		},
   547  		{
   548  			name: "deleted non-empty chain",
   549  			input: dedent.Dedent(`
   550  				*filter
   551  				:KUBE-SERVICES - [0:0]
   552  				:KUBE-EXTERNAL-SERVICES - [0:0]
   553  				:KUBE-FORWARD - [0:0]
   554  				:KUBE-NODEPORTS - [0:0]
   555  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   556  				-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   557  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   558  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   559  				-X KUBE-FORWARD
   560  				COMMIT
   561  				`),
   562  			error: `cannot delete chain "KUBE-FORWARD" after adding rules`,
   563  		},
   564  		{
   565  			name: "junk rule",
   566  			input: dedent.Dedent(`
   567  				*filter
   568  				:KUBE-SERVICES - [0:0]
   569  				:KUBE-EXTERNAL-SERVICES - [0:0]
   570  				:KUBE-FORWARD - [0:0]
   571  				:KUBE-NODEPORTS - [0:0]
   572  				-A KUBE-NODEPORTS -m comment --comment "ns2/svc2:p80 health check node port" -m tcp -p tcp --dport 30000 -j ACCEPT
   573  				-Q KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
   574  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
   575  				-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   576  				COMMIT
   577  				`),
   578  			error: `"-Q KUBE-FORWARD`,
   579  		},
   580  	} {
   581  		t.Run(tc.name, func(t *testing.T) {
   582  			dump, err := ParseIPTablesDump(tc.input)
   583  			if err == nil {
   584  				if tc.error != "" {
   585  					t.Errorf("unexpectedly did not get error")
   586  				} else if !reflect.DeepEqual(tc.output, dump) {
   587  					t.Errorf("bad output: expected %#v got %#v", tc.output, dump)
   588  				}
   589  			} else {
   590  				if tc.error == "" {
   591  					t.Errorf("got unexpected error: %v", err)
   592  				} else if !strings.Contains(err.Error(), tc.error) {
   593  					t.Errorf("got wrong error: %v (expected %q)", err, tc.error)
   594  				}
   595  			}
   596  		})
   597  	}
   598  }
   599  

View as plain text