| //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 ( |
| "bytes" |
| "strings" |
| "testing" |
| |
| "github.com/lithammer/dedent" |
| |
| "k8s.io/kubernetes/pkg/util/iptables" |
| ) |
| |
| func TestFakeIPTables(t *testing.T) { |
| fake := NewFake() |
| buf := bytes.NewBuffer(nil) |
| |
| err := fake.SaveInto("", buf) |
| if err != nil { |
| t.Fatalf("unexpected error from SaveInto: %v", err) |
| } |
| expected := dedent.Dedent(strings.Trim(` |
| *nat |
| :PREROUTING - [0:0] |
| :INPUT - [0:0] |
| :OUTPUT - [0:0] |
| :POSTROUTING - [0:0] |
| COMMIT |
| *filter |
| :INPUT - [0:0] |
| :FORWARD - [0:0] |
| :OUTPUT - [0:0] |
| COMMIT |
| *mangle |
| COMMIT |
| `, "\n")) |
| if buf.String() != expected { |
| t.Fatalf("bad initial dump. expected:\n%s\n\ngot:\n%s\n", expected, buf.Bytes()) |
| } |
| |
| // EnsureChain |
| existed, err := fake.EnsureChain(iptables.Table("blah"), iptables.Chain("KUBE-TEST")) |
| if err == nil { |
| t.Errorf("did not get expected error creating chain in non-existent table") |
| } else if existed { |
| t.Errorf("wrong return value from EnsureChain with non-existent table") |
| } |
| existed, err = fake.EnsureChain(iptables.TableNAT, iptables.Chain("KUBE-TEST")) |
| if err != nil { |
| t.Errorf("unexpected error creating chain: %v", err) |
| } else if existed { |
| t.Errorf("wrong return value from EnsureChain with non-existent chain") |
| } |
| existed, err = fake.EnsureChain(iptables.TableNAT, iptables.Chain("KUBE-TEST")) |
| if err != nil { |
| t.Errorf("unexpected error creating chain: %v", err) |
| } else if !existed { |
| t.Errorf("wrong return value from EnsureChain with existing chain") |
| } |
| |
| // ChainExists |
| exists, err := fake.ChainExists(iptables.TableNAT, iptables.Chain("KUBE-TEST")) |
| if err != nil { |
| t.Errorf("unexpected error checking chain: %v", err) |
| } else if !exists { |
| t.Errorf("wrong return value from ChainExists with existing chain") |
| } |
| exists, err = fake.ChainExists(iptables.TableNAT, iptables.Chain("KUBE-TEST-NOT")) |
| if err != nil { |
| t.Errorf("unexpected error checking chain: %v", err) |
| } else if exists { |
| t.Errorf("wrong return value from ChainExists with non-existent chain") |
| } |
| |
| // EnsureRule |
| existed, err = fake.EnsureRule(iptables.Append, iptables.Table("blah"), iptables.Chain("KUBE-TEST"), "-j", "ACCEPT") |
| if err == nil { |
| t.Errorf("did not get expected error creating rule in non-existent table") |
| } else if existed { |
| t.Errorf("wrong return value from EnsureRule with non-existent table") |
| } |
| existed, err = fake.EnsureRule(iptables.Append, iptables.TableNAT, iptables.Chain("KUBE-TEST-NOT"), "-j", "ACCEPT") |
| if err == nil { |
| t.Errorf("did not get expected error creating rule in non-existent chain") |
| } else if existed { |
| t.Errorf("wrong return value from EnsureRule with non-existent chain") |
| } |
| existed, err = fake.EnsureRule(iptables.Append, iptables.TableNAT, iptables.Chain("KUBE-TEST"), "-j", "ACCEPT") |
| if err != nil { |
| t.Errorf("unexpected error creating rule: %v", err) |
| } else if existed { |
| t.Errorf("wrong return value from EnsureRule with non-existent rule") |
| } |
| existed, err = fake.EnsureRule(iptables.Prepend, iptables.TableNAT, iptables.Chain("KUBE-TEST"), "-j", "DROP") |
| if err != nil { |
| t.Errorf("unexpected error creating rule: %v", err) |
| } else if existed { |
| t.Errorf("wrong return value from EnsureRule with non-existent rule") |
| } |
| existed, err = fake.EnsureRule(iptables.Append, iptables.TableNAT, iptables.Chain("KUBE-TEST"), "-j", "DROP") |
| if err != nil { |
| t.Errorf("unexpected error creating rule: %v", err) |
| } else if !existed { |
| t.Errorf("wrong return value from EnsureRule with already-existing rule") |
| } |
| |
| // Sanity-check... |
| buf.Reset() |
| err = fake.SaveInto("", buf) |
| if err != nil { |
| t.Fatalf("unexpected error from SaveInto: %v", err) |
| } |
| expected = dedent.Dedent(strings.Trim(` |
| *nat |
| :PREROUTING - [0:0] |
| :INPUT - [0:0] |
| :OUTPUT - [0:0] |
| :POSTROUTING - [0:0] |
| :KUBE-TEST - [0:0] |
| -A KUBE-TEST -j DROP |
| -A KUBE-TEST -j ACCEPT |
| COMMIT |
| *filter |
| :INPUT - [0:0] |
| :FORWARD - [0:0] |
| :OUTPUT - [0:0] |
| COMMIT |
| *mangle |
| COMMIT |
| `, "\n")) |
| if buf.String() != expected { |
| t.Fatalf("bad sanity-check dump. expected:\n%s\n\ngot:\n%s\n", expected, buf.Bytes()) |
| } |
| |
| // DeleteRule |
| err = fake.DeleteRule(iptables.Table("blah"), iptables.Chain("KUBE-TEST"), "-j", "DROP") |
| if err == nil { |
| t.Errorf("did not get expected error deleting rule in non-existent table") |
| } |
| err = fake.DeleteRule(iptables.TableNAT, iptables.Chain("KUBE-TEST-NOT"), "-j", "DROP") |
| if err == nil { |
| t.Errorf("did not get expected error deleting rule in non-existent chain") |
| } |
| err = fake.DeleteRule(iptables.TableNAT, iptables.Chain("KUBE-TEST"), "-j", "DROPLET") |
| if err != nil { |
| t.Errorf("unexpected error deleting non-existent rule: %v", err) |
| } |
| err = fake.DeleteRule(iptables.TableNAT, iptables.Chain("KUBE-TEST"), "-j", "DROP") |
| if err != nil { |
| t.Errorf("unexpected error deleting rule: %v", err) |
| } |
| |
| // Restore |
| rules := dedent.Dedent(strings.Trim(` |
| *nat |
| :KUBE-RESTORED - [0:0] |
| :KUBE-MISC-CHAIN - [0:0] |
| :KUBE-MISC-TWO - [0:0] |
| :KUBE-EMPTY - [0:0] |
| -A KUBE-RESTORED -m comment --comment "restored chain" -j ACCEPT |
| -A KUBE-MISC-CHAIN -s 1.2.3.4 -j KUBE-MISC-TWO |
| -A KUBE-MISC-CHAIN -d 5.6.7.8 -j MASQUERADE |
| -A KUBE-MISC-TWO -j ACCEPT |
| COMMIT |
| `, "\n")) |
| err = fake.Restore(iptables.TableNAT, []byte(rules), iptables.NoFlushTables, iptables.NoRestoreCounters) |
| if err != nil { |
| t.Fatalf("unexpected error from Restore: %v", err) |
| } |
| |
| // We used NoFlushTables, so this should leave KUBE-TEST unchanged |
| buf.Reset() |
| err = fake.SaveInto("", buf) |
| if err != nil { |
| t.Fatalf("unexpected error from SaveInto: %v", err) |
| } |
| expected = dedent.Dedent(strings.Trim(` |
| *nat |
| :PREROUTING - [0:0] |
| :INPUT - [0:0] |
| :OUTPUT - [0:0] |
| :POSTROUTING - [0:0] |
| :KUBE-TEST - [0:0] |
| :KUBE-RESTORED - [0:0] |
| :KUBE-MISC-CHAIN - [0:0] |
| :KUBE-MISC-TWO - [0:0] |
| :KUBE-EMPTY - [0:0] |
| -A KUBE-TEST -j ACCEPT |
| -A KUBE-RESTORED -m comment --comment "restored chain" -j ACCEPT |
| -A KUBE-MISC-CHAIN -s 1.2.3.4 -j KUBE-MISC-TWO |
| -A KUBE-MISC-CHAIN -d 5.6.7.8 -j MASQUERADE |
| -A KUBE-MISC-TWO -j ACCEPT |
| COMMIT |
| *filter |
| :INPUT - [0:0] |
| :FORWARD - [0:0] |
| :OUTPUT - [0:0] |
| COMMIT |
| *mangle |
| COMMIT |
| `, "\n")) |
| if buf.String() != expected { |
| t.Fatalf("bad post-restore dump. expected:\n%s\n\ngot:\n%s\n", expected, buf.Bytes()) |
| } |
| |
| // Trying to use Restore to delete a chain that another chain jumps to will fail |
| rules = dedent.Dedent(strings.Trim(` |
| *nat |
| :KUBE-MISC-TWO - [0:0] |
| -X KUBE-MISC-TWO |
| COMMIT |
| `, "\n")) |
| err = fake.Restore(iptables.TableNAT, []byte(rules), iptables.NoFlushTables, iptables.RestoreCounters) |
| if err == nil || !strings.Contains(err.Error(), "referenced by existing rules") { |
| t.Fatalf("Expected 'referenced by existing rules' error from Restore, got %v", err) |
| } |
| |
| // Trying to use Restore to add a jump to a non-existent chain will fail |
| rules = dedent.Dedent(strings.Trim(` |
| *nat |
| :KUBE-MISC-TWO - [0:0] |
| -A KUBE-MISC-TWO -j KUBE-MISC-THREE |
| COMMIT |
| `, "\n")) |
| err = fake.Restore(iptables.TableNAT, []byte(rules), iptables.NoFlushTables, iptables.RestoreCounters) |
| if err == nil || !strings.Contains(err.Error(), "non-existent chain") { |
| t.Fatalf("Expected 'non-existent chain' error from Restore, got %v", err) |
| } |
| |
| // more Restore; empty out one chain and delete another, but also update its counters |
| rules = dedent.Dedent(strings.Trim(` |
| *nat |
| :KUBE-RESTORED - [0:0] |
| :KUBE-TEST - [99:9999] |
| -X KUBE-RESTORED |
| COMMIT |
| `, "\n")) |
| err = fake.Restore(iptables.TableNAT, []byte(rules), iptables.NoFlushTables, iptables.RestoreCounters) |
| if err != nil { |
| t.Fatalf("unexpected error from Restore: %v", err) |
| } |
| |
| buf.Reset() |
| err = fake.SaveInto("", buf) |
| if err != nil { |
| t.Fatalf("unexpected error from SaveInto: %v", err) |
| } |
| expected = dedent.Dedent(strings.Trim(` |
| *nat |
| :PREROUTING - [0:0] |
| :INPUT - [0:0] |
| :OUTPUT - [0:0] |
| :POSTROUTING - [0:0] |
| :KUBE-TEST - [99:9999] |
| :KUBE-MISC-CHAIN - [0:0] |
| :KUBE-MISC-TWO - [0:0] |
| :KUBE-EMPTY - [0:0] |
| -A KUBE-MISC-CHAIN -s 1.2.3.4 -j KUBE-MISC-TWO |
| -A KUBE-MISC-CHAIN -d 5.6.7.8 -j MASQUERADE |
| -A KUBE-MISC-TWO -j ACCEPT |
| COMMIT |
| *filter |
| :INPUT - [0:0] |
| :FORWARD - [0:0] |
| :OUTPUT - [0:0] |
| COMMIT |
| *mangle |
| COMMIT |
| `, "\n")) |
| if buf.String() != expected { |
| t.Fatalf("bad post-second-restore dump. expected:\n%s\n\ngot:\n%s\n", expected, buf.Bytes()) |
| } |
| |
| // RestoreAll, FlushTables |
| rules = dedent.Dedent(strings.Trim(` |
| *filter |
| :INPUT - [0:0] |
| :FORWARD - [0:0] |
| :OUTPUT - [0:0] |
| :KUBE-TEST - [0:0] |
| -A KUBE-TEST -m comment --comment "filter table KUBE-TEST" -j ACCEPT |
| COMMIT |
| *nat |
| :PREROUTING - [0:0] |
| :INPUT - [0:0] |
| :OUTPUT - [0:0] |
| :POSTROUTING - [0:0] |
| :KUBE-TEST - [88:8888] |
| :KUBE-NEW-CHAIN - [0:0] |
| -A KUBE-NEW-CHAIN -d 172.30.0.1 -j DNAT --to-destination 10.0.0.1 |
| -A KUBE-NEW-CHAIN -d 172.30.0.2 -j DNAT --to-destination 10.0.0.2 |
| -A KUBE-NEW-CHAIN -d 172.30.0.3 -j DNAT --to-destination 10.0.0.3 |
| COMMIT |
| `, "\n")) |
| err = fake.RestoreAll([]byte(rules), iptables.FlushTables, iptables.NoRestoreCounters) |
| if err != nil { |
| t.Fatalf("unexpected error from RestoreAll: %v", err) |
| } |
| |
| buf.Reset() |
| err = fake.SaveInto("", buf) |
| if err != nil { |
| t.Fatalf("unexpected error from SaveInto: %v", err) |
| } |
| expected = dedent.Dedent(strings.Trim(` |
| *nat |
| :PREROUTING - [0:0] |
| :INPUT - [0:0] |
| :OUTPUT - [0:0] |
| :POSTROUTING - [0:0] |
| :KUBE-TEST - [88:8888] |
| :KUBE-NEW-CHAIN - [0:0] |
| -A KUBE-NEW-CHAIN -d 172.30.0.1 -j DNAT --to-destination 10.0.0.1 |
| -A KUBE-NEW-CHAIN -d 172.30.0.2 -j DNAT --to-destination 10.0.0.2 |
| -A KUBE-NEW-CHAIN -d 172.30.0.3 -j DNAT --to-destination 10.0.0.3 |
| COMMIT |
| *filter |
| :INPUT - [0:0] |
| :FORWARD - [0:0] |
| :OUTPUT - [0:0] |
| :KUBE-TEST - [0:0] |
| -A KUBE-TEST -m comment --comment "filter table KUBE-TEST" -j ACCEPT |
| COMMIT |
| *mangle |
| COMMIT |
| `, "\n")) |
| if buf.String() != expected { |
| t.Fatalf("bad post-restore-all dump. expected:\n%s\n\ngot:\n%s\n", expected, buf.Bytes()) |
| } |
| } |