diff --git a/EKS_DISTRO_TAG_FILE.yaml b/EKS_DISTRO_TAG_FILE.yaml index 83989e664..9eb4f5c2e 100644 --- a/EKS_DISTRO_TAG_FILE.yaml +++ b/EKS_DISTRO_TAG_FILE.yaml @@ -3,7 +3,7 @@ al2: eks-distro-minimal-base: 2023-02-22-1677092456.2 eks-distro-minimal-base-nonroot: 2023-02-22-1677092456.2 eks-distro-minimal-base-glibc: 2023-02-22-1677092456.2 - eks-distro-minimal-base-iptables: 2023-02-22-1677092456.2 + eks-distro-minimal-base-iptables: null eks-distro-minimal-base-docker-client: 2023-02-22-1677092456.2 eks-distro-minimal-base-csi: 2023-02-22-1677092456.2 eks-distro-minimal-base-csi-ebs: 2023-02-22-1677092456.2 @@ -50,7 +50,7 @@ al2022: eks-distro-minimal-base: 2022-12-15-1671132599.2022 eks-distro-minimal-base-nonroot: 2022-12-15-1671132599.2022 eks-distro-minimal-base-glibc: 2022-12-15-1671132599.2022 - eks-distro-minimal-base-iptables: 2022-12-15-1671132599.2022 + eks-distro-minimal-base-iptables: null eks-distro-minimal-base-docker-client: 2022-12-15-1671132599.2022 eks-distro-minimal-base-csi: 2023-02-01-1675278096.2022 eks-distro-minimal-base-csi-ebs: 2022-12-15-1671132599.2022 diff --git a/eks-distro-base/Dockerfile.minimal-base-iptables b/eks-distro-base/Dockerfile.minimal-base-iptables index 9b4aa48fe..20bc1a072 100644 --- a/eks-distro-base/Dockerfile.minimal-base-iptables +++ b/eks-distro-base/Dockerfile.minimal-base-iptables @@ -16,6 +16,18 @@ # *NOTE* we have to limit our number of layers heres because in presubmits there # is no overlay fs and we will run out of space quickly +################# IPTABLES WRAPPER BUILDER ##################### + +ARG BUILDER_IMAGE +ARG BASE_IMAGE + +FROM public.ecr.aws/eks-distro-build-tooling/golang:1.20 as iptables-wrapper-builder + +ADD iptables-wrappers/ /iptables-wrappers +RUN set -x && \ + cd /iptables-wrappers && \ + make build + ################# BUILDER ##################### ARG BASE_IMAGE=unused ARG BUILDER_IMAGE=unused @@ -28,6 +40,8 @@ ARG OUTPUT_DEBUG_LOG # built it has the latest scripts in the builder COPY scripts/ /usr/bin +COPY --from=iptables-wrapper-builder /iptables-wrappers/bin/iptables-wrapper /newroot/usr/sbin + RUN set -x && \ export OUTPUT_DEBUG_LOG=${OUTPUT_DEBUG_LOG} && \ if_al2 install_rpm chkconfig && \ @@ -38,15 +52,29 @@ RUN set -x && \ iptables-nft && \ if_al2 install_rpm iptables ebtables && \ if_al2022 install_rpm iptables-legacy ebtables-legacy && \ - # AL2 doesn't setup the ip6tables alternative the same as ubuntu/debian - # which is what the iptables-wrapper script provided by kubernetes expects - # since kind's entrypoint assumes this adding the alternative - # adding the alternative for ip6tabels here so the --set calls would work later on as expected + # The original iptables-wrapper.sh script assumed that there were both iptables and ip6tables + # alternatives setup since the upstream image was based on debian which sets them up that way + # AL2 follows RHEL which uses a single alternaive setup for both iptables and ip6tables + # The wrapper script is currently only used by the eks-a kind image, since the other consumers of this base image + # do not include bash and have therefore relied on legacy as the default + # This adds the ip6tables alternative so that the `update-alternatives --set` calls which happen during the kind + # bootstraping process via the iptables-wrapper.sh will work successfully # https://github.com/kubernetes/release/blob/master/images/build/debian-iptables/buster/iptables-wrapper for m in nft legacy; do chroot $NEWROOT update-alternatives --install /usr/sbin/ip6tables ip6tables /usr/sbin/ip6tables-$m 10 \ --slave /usr/sbin/ip6tables-restore ip6tables-restore /usr/sbin/ip6tables-$m-restore \ --slave /usr/sbin/ip6tables-save ip6tables-save /usr/sbin/ip6tables-$m-save; done && \ - # ensure iptables are in legacy mode + # The newer version of the iptables-wrapper.sh: https://github.com/kubernetes-sigs/iptables-wrappers/blob/master/iptables-wrapper-installer.sh + # And the in-progress golang version supports RHEL's configuration of alternatives, where iptables and ip6tables is managed via the same record + # We are now including the built golang based iptables-wrapper as a possible alternative for downstream consumers (eks-d kube-proxy, eks addon kube-proxy, eks-a kind image) + # Since this version supports a single alternative record, we are adding the iptables-wrapper as a single record which is different than the above which is left for backwards compat + # Not setting it as the default, but intend to in the future once all of our known consumers have decided to take this as their default mode + chroot $NEWROOT update-alternatives --install /usr/sbin/iptables iptables /usr/sbin/iptables-wrapper 100 \ + --slave /usr/sbin/iptables-restore iptables-restore /usr/sbin/iptables-wrapper \ + --slave /usr/sbin/iptables-save iptables-save /usr/sbin/iptables-wrapper \ + --slave /usr/sbin/ip6tables ip6tables /usr/sbin/iptables-wrapper \ + --slave /usr/sbin/ip6tables-restore ip6tables-restore /usr/sbin/iptables-wrapper \ + --slave /usr/sbin/ip6tables-save ip6tables-save /usr/sbin/iptables-wrapper && \ + # default to iptables in legacy mode for t in iptables ip6tables; do chroot $NEWROOT update-alternatives --set $t /usr/sbin/$t-legacy; done && \ # Remove bad symlinks due to deleted man files find $NEWROOT/etc/alternatives -xtype l -name "*-man" -delete -print && \ diff --git a/eks-distro-base/IPTABLES_WRAPPER_UPSTREAM.md b/eks-distro-base/IPTABLES_WRAPPER_UPSTREAM.md new file mode 100644 index 000000000..035254bb7 --- /dev/null +++ b/eks-distro-base/IPTABLES_WRAPPER_UPSTREAM.md @@ -0,0 +1,10 @@ +This has been copied in its entirety from https://github.com/kubernetes-sigs/iptables-wrappers/pull/6 which was contributed by +EKS-Anywhere engineer, https://github.com/g-gaston. Once merged upstream this will be changed to pull from the upstream repo. +Hardcoding for now to avoid the branch going away or changing and breaking future builds. + +Manual steps to pull: +- git clone https://github.com/g-gaston/iptables-wrappers.git iptables-wrapper-upstream && \ + rm -rf iptables-wrappers && mkdir iptables-wrappers && \ + git -C iptables-wrapper-upstream checkout go-wrapper && \ + cp -rf iptables-wrapper-upstream/{internal,go.mod,LICENSE,main.go,Makefile,test} iptables-wrappers && \ + rm -rf iptables-wrapper-upstream \ No newline at end of file diff --git a/eks-distro-base/iptables-wrappers/LICENSE b/eks-distro-base/iptables-wrappers/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/eks-distro-base/iptables-wrappers/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/eks-distro-base/iptables-wrappers/Makefile b/eks-distro-base/iptables-wrappers/Makefile new file mode 100644 index 000000000..95a11da8b --- /dev/null +++ b/eks-distro-base/iptables-wrappers/Makefile @@ -0,0 +1,42 @@ +BIN_DIR ?= bin +GO ?= go + +all: fmt vet check + +$(BIN_DIR): + mkdir -p $(BIN_DIR) + +build: $(BIN_DIR) + CGO_ENABLED=0 $(GO) build -ldflags='-s -w -extldflags="-static" -buildid=""' -trimpath -o $(BIN_DIR)/iptables-wrapper github.com/kubernetes-sigs/iptables-wrappers + +vet: ## Run go vet against code. + $(GO) vet ./... + +fmt: ## Check formatting + if [ "$$(gofmt -e -l . | tee /dev/tty | wc -l)" -gt 0 ]; then \ + echo "Go files need formatting"; \ + exit 1; \ + fi + +build-tests: $(BIN_DIR) + $(GO) test ./test -c -o $(BIN_DIR)/tests + +check: check-debian check-debian-nosanity check-debian-backports check-fedora check-alpine + +check-debian: build build-tests + ./test/run-test.sh --build-fail debian + +check-debian-nosanity: build build-tests + ./test/run-test.sh --build-arg="INSTALL_ARGS=--no-sanity-check" --nft-fail debian-nosanity + +check-debian-backports: build build-tests + ./test/run-test.sh --build-arg="REPO=buster-backports" debian-backports + +check-fedora: build build-tests + ./test/run-test.sh fedora + +check-alpine: build build-tests + ./test/run-test.sh alpine + +check-distroless: build build-tests + ./test/run-test.sh distroless diff --git a/eks-distro-base/iptables-wrappers/go.mod b/eks-distro-base/iptables-wrappers/go.mod new file mode 100644 index 000000000..64deadac3 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/go.mod @@ -0,0 +1,3 @@ +module github.com/kubernetes-sigs/iptables-wrappers + +go 1.19 diff --git a/eks-distro-base/iptables-wrappers/internal/commands/run.go b/eks-distro-base/iptables-wrappers/internal/commands/run.go new file mode 100644 index 000000000..512dbc46b --- /dev/null +++ b/eks-distro-base/iptables-wrappers/internal/commands/run.go @@ -0,0 +1,38 @@ +/* +Copyright 2023 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 commands + +import ( + "bytes" + "fmt" + "os/exec" +) + +// RunAndReadError runs a exec.Cmd and tries to extract the error message from stderr +// if present and it includes it in the returned error. This overrides the Stderr in cmd if +// present. +func RunAndReadError(cmd *exec.Cmd) error { + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + if stderr.Len() > 0 { + err = fmt.Errorf("%s: %v", stderr.String(), err) + } + + return err + } + + return nil +} diff --git a/eks-distro-base/iptables-wrappers/internal/files/exist.go b/eks-distro-base/iptables-wrappers/internal/files/exist.go new file mode 100644 index 000000000..c3bb7b7a6 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/internal/files/exist.go @@ -0,0 +1,22 @@ +/* +Copyright 2023 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 files + +import "os" + +// ExecutableExists checks if a file exists and it's executable by someone. +func ExecutableExists(path string) bool { + stat, err := os.Stat(path) + return err == nil && stat.Mode()&0o111 != 0 +} diff --git a/eks-distro-base/iptables-wrappers/internal/iptables/alternatives.go b/eks-distro-base/iptables-wrappers/internal/iptables/alternatives.go new file mode 100644 index 000000000..655ba09f3 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/internal/iptables/alternatives.go @@ -0,0 +1,106 @@ +/* +Copyright 2023 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 iptables + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/kubernetes-sigs/iptables-wrappers/internal/commands" + "github.com/kubernetes-sigs/iptables-wrappers/internal/files" +) + +// AlternativeSelector allows to configure a system to use iptables in +// nft or legacy mode. +type AlternativeSelector interface { + // UseMode configures the system to use the selected iptables mode. + UseMode(ctx context.Context, mode Mode) error +} + +// BuildAlternativeSelector builds the proper iptablesAlternativeSelector depending +// on the machine's setup. It will use either `alternatives` or `update-alternatives` if present +// in the sbin folder. If none is present, it will manage iptables binaries by manually +// creating symlinks. +func BuildAlternativeSelector(sbinPath string) AlternativeSelector { + if files.ExecutableExists(filepath.Join(sbinPath, "alternatives")) { + return alternativesSelector{sbinPath: sbinPath} + } else if files.ExecutableExists(filepath.Join(sbinPath, "update-alternatives")) { + return updateAlternativesSelector{sbinPath: sbinPath} + } else { + // if we don't find any tool to managed the alternatives, handle it manually with symlinks + return symlinkSelector{sbinPath: sbinPath} + } +} + +// updateAlternativesSelector manages an iptables setup by using the `update-alternatives` binary. +// This is most common for debian based OSs. +type updateAlternativesSelector struct { + sbinPath string +} + +func (u updateAlternativesSelector) UseMode(ctx context.Context, mode Mode) error { + modeStr := string(mode) + + if err := commands.RunAndReadError(exec.CommandContext(ctx, "update-alternatives", "--set", "iptables", filepath.Join(u.sbinPath, "iptables-"+modeStr))); err != nil { + return fmt.Errorf("update-alternatives iptables to mode %s: %v", modeStr, err) + } + + if err := commands.RunAndReadError(exec.CommandContext(ctx, "update-alternatives", "--set", "ip6tables", filepath.Join(u.sbinPath, "ip6tables-"+modeStr))); err != nil { + return fmt.Errorf("update-alternatives ip6tables to mode %s: %v", modeStr, err) + } + + return nil +} + +// alternativesSelector manages an iptables setup by using the `alternatives` binary. +// This is most common for fedora based OSs. +type alternativesSelector struct { + sbinPath string +} + +func (a alternativesSelector) UseMode(ctx context.Context, mode Mode) error { + if err := commands.RunAndReadError(exec.CommandContext(ctx, "alternatives", "--set", "iptables", filepath.Join(a.sbinPath, "iptables-"+string(mode)))); err != nil { + return fmt.Errorf("alternatives to update iptables to mode %s: %v", string(mode), err) + } + return nil +} + +// symlinkSelector manages an iptables setup by manually creating symlinks +// that point to the proper "mode" binaries. +// It configures: `iptables`, `iptables-save`, `iptables-restore`, +// `ip6tables`, `ip6tables-save` and `ip6tables-restore`. +type symlinkSelector struct { + sbinPath string +} + +func (s symlinkSelector) UseMode(ctx context.Context, mode Mode) error { + modeStr := string(mode) + xtablesForModePath := XtablesPath(s.sbinPath, mode) + cmds := []string{"iptables", "iptables-save", "iptables-restore", "ip6tables", "ip6tables-save", "ip6tables-restore"} + + for _, cmd := range cmds { + cmdPath := filepath.Join(s.sbinPath, cmd) + // If deleting fails, ignore it and try to create symlink regardless + _ = os.RemoveAll(cmdPath) + + if err := os.Symlink(xtablesForModePath, cmdPath); err != nil { + return fmt.Errorf("creating %s symlink for mode %s: %v", cmd, modeStr, err) + } + } + + return nil +} diff --git a/eks-distro-base/iptables-wrappers/internal/iptables/detect.go b/eks-distro-base/iptables-wrappers/internal/iptables/detect.go new file mode 100644 index 000000000..67e2d6024 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/internal/iptables/detect.go @@ -0,0 +1,87 @@ +/* +Copyright 2023 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 iptables + +import ( + "bytes" + "context" + "errors" + + "github.com/kubernetes-sigs/iptables-wrappers/internal/files" +) + +// DetectBinaryDir tries to detect the `iptables` location in +// either /usr/sbin or /sbin. If it's not there, it returns an error. +func DetectBinaryDir() (string, error) { + if files.ExecutableExists("/usr/sbin/iptables") { + return "/usr/sbin", nil + } else if files.ExecutableExists("/sbin/iptables") { + return "/sbin", nil + } else { + return "", errors.New("iptables is not present in either /usr/sbin or /sbin") + } +} + +// Mode represents the two different modes iptables can be +// configured to: nft or legacy. In string form it can be used to +// to complete all `iptables-*` commands. +type Mode string + +const ( + Legacy Mode = "legacy" + NFT Mode = "nft" +) + +// DetectMode inspects the current iptables entries and tries to +// guess which iptables mode is being used: legacy or nft +func DetectMode(ctx context.Context, iptables Installation) Mode { + // This method ignores all errors, this is on purpose. We execute all commands + // and try to detect patterns in a best effort basis. If somthing fails, + // continue with the next step. Worse case scenario if everything fails, + // default to nft. + + // In kubernetes 1.17 and later, kubelet will have created at least + // one chain in the "mangle" table (either "KUBE-IPTABLES-HINT" or + // "KUBE-KUBELET-CANARY"), so check that first, against + // iptables-nft, because we can check that more efficiently and + // it's more common these days. + rulesOutput := &bytes.Buffer{} + _ = iptables.NFTSave(ctx, rulesOutput, "-t", "mangle") + if hasKubeletChains(rulesOutput.Bytes()) { + return NFT + } + rulesOutput.Reset() + _ = iptables.NFTSaveIP6(ctx, rulesOutput, "-t", "mangle") + if hasKubeletChains(rulesOutput.Bytes()) { + return NFT + } + rulesOutput.Reset() + + // Check for kubernetes 1.17-or-later with iptables-legacy. We + // can't pass "-t mangle" to iptables-legacy-save because it would + // cause the kernel to create that table if it didn't already + // exist, which we don't want. So we have to grab all the rules. + _ = iptables.LegacySave(ctx, rulesOutput) + if hasKubeletChains(rulesOutput.Bytes()) { + return Legacy + } + rulesOutput.Reset() + _ = iptables.LegacySaveIP6(ctx, rulesOutput) + if hasKubeletChains(rulesOutput.Bytes()) { + return Legacy + } + + // If we can't detect any of the 2 patterns, default to nft. + return NFT +} diff --git a/eks-distro-base/iptables-wrappers/internal/iptables/rules.go b/eks-distro-base/iptables-wrappers/internal/iptables/rules.go new file mode 100644 index 000000000..528f33795 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/internal/iptables/rules.go @@ -0,0 +1,33 @@ +/* +Copyright 2023 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 iptables + +import "regexp" + +var ( + kubeletChainsRegex = regexp.MustCompile(`(?m)^:(KUBE-IPTABLES-HINT|KUBE-KUBELET-CANARY)`) + ruleEntryRegex = regexp.MustCompile(`(?m)^-`) +) + +// hasKubeletChains checks if the output of an iptables*-save command +// contains any of the rules set by kubelet. +func hasKubeletChains(output []byte) bool { + return kubeletChainsRegex.Match(output) +} + +// ruleEntriesNum counts how many rules there are in an iptables*-save command +// output. +func ruleEntriesNum(iptablesOutput []byte) int { + return len(ruleEntryRegex.FindAllIndex(iptablesOutput, -1)) +} diff --git a/eks-distro-base/iptables-wrappers/internal/iptables/xtables.go b/eks-distro-base/iptables-wrappers/internal/iptables/xtables.go new file mode 100644 index 000000000..468439e07 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/internal/iptables/xtables.go @@ -0,0 +1,89 @@ +/* +Copyright 2023 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 iptables + +import ( + "bytes" + "context" + "os/exec" + "path/filepath" + + "github.com/kubernetes-sigs/iptables-wrappers/internal/commands" +) + +const ( + xtablesNFTMultiBinaryName = "xtables-nft-multi" + xtablesLegacyMultiBinaryName = "xtables-legacy-multi" +) + +// Installation represents the set of iptables-*-save binaries installed in a machine. +// It is expected the machine supports both nft and legacy modes. This can be implemented by +// calling directly iptables-*-save, xtables, etc. The implementation should accept the same +// command arguments as the mentioned binaries. +type Installation interface { + // LegacySave runs a iptables-legacy-save command + LegacySave(ctx context.Context, out *bytes.Buffer, args ...string) error + // LegacySaveIP6 runs a ip6tables-legacy-save command + LegacySaveIP6(ctx context.Context, out *bytes.Buffer, args ...string) error + // NFTSave runs a iptables-nft-save command + NFTSave(ctx context.Context, out *bytes.Buffer, args ...string) error + // NFTSaveIP6 runs a ip6tables-nft-save command + NFTSaveIP6(ctx context.Context, out *bytes.Buffer, args ...string) error +} + +func NewXtablesMultiInstallation(sbinPath string) XtablesMulti { + return XtablesMulti{ + nftBinary: filepath.Join(sbinPath, xtablesNFTMultiBinaryName), + legacyBinary: filepath.Join(sbinPath, xtablesLegacyMultiBinaryName), + } +} + +// XtablesMulti allows to run iptables commands using xtables-*-multi. +// It implements iptablesInstallation. +type XtablesMulti struct { + nftBinary string + legacyBinary string +} + +func (x XtablesMulti) LegacySave(ctx context.Context, out *bytes.Buffer, args ...string) error { + return x.exec(ctx, out, x.legacyBinary, "iptables-save", args...) +} + +func (x XtablesMulti) LegacySaveIP6(ctx context.Context, out *bytes.Buffer, args ...string) error { + return x.exec(ctx, out, x.legacyBinary, "ip6tables-save", args...) +} + +func (x XtablesMulti) NFTSave(ctx context.Context, out *bytes.Buffer, args ...string) error { + return x.exec(ctx, out, x.nftBinary, "iptables-save", args...) +} + +func (x XtablesMulti) NFTSaveIP6(ctx context.Context, out *bytes.Buffer, args ...string) error { + return x.exec(ctx, out, x.nftBinary, "ip6tables-save", args...) +} + +func (x XtablesMulti) exec(ctx context.Context, out *bytes.Buffer, multiBinary, command string, args ...string) error { + allArgs := make([]string, 0, len(args)+1) + allArgs = append(allArgs, command) + allArgs = append(allArgs, args...) + + c := exec.CommandContext(ctx, multiBinary, allArgs...) + c.Stdout = out + + return commands.RunAndReadError(c) +} + +// XtablesPath returns the path to the `xtable--multi binary +func XtablesPath(sbinPath string, mode Mode) string { + return filepath.Join(sbinPath, "xtables-"+string(mode)+"-multi") +} diff --git a/eks-distro-base/iptables-wrappers/main.go b/eks-distro-base/iptables-wrappers/main.go new file mode 100644 index 000000000..caebb0552 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/main.go @@ -0,0 +1,93 @@ +/* +Copyright 2023 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. +*/ + +/* +Iptables-wrapper tries to detect which iptables mode is being used by the host +even when being run from a container. It then updates the iptables commands to +point to the right binaries for that mode. Before exiting it re-executes the given +command. + +The process is as follows: + 1. Calls `xtables--multi` and checks if the kubelet rules exists. + It searches for different patterns in the configured rules, trying to match different + kubernetes versions, and it uses the results to guess which mode is in use. + 2. Updates the alternatives/symlinks to point to the proper binaries for the detected mode. + Depending on the OS it uses `update-alternatives`, `alternatives` or it manually creates symlinks. + 3. Re-execs the original command received by this binary. + +We assume this binary has been symlinked to some/all iptables binaries and whatever was received +here was intended to be an iptables-* command. If that is not the case and this command is either +executed directly or through a symlink that doesn't point to an iptables binary, +it will enter an infinite loop, calling itself recursively. + +It's important to note that this proxy behavior will only happen on the first iptables-* +execution. Following invocations will use directly the binaries for the selected mode. +*/ +package main + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + + "github.com/kubernetes-sigs/iptables-wrappers/internal/iptables" +) + +func main() { + ctx := context.Background() + + sbinPath, err := iptables.DetectBinaryDir() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", err) + os.Exit(1) + } + + // We use `xtables--multi` binaries by default to inspect the installed rules, + // but this can be changed to directly use `iptables--save` binaries. + mode := iptables.DetectMode(ctx, iptables.NewXtablesMultiInstallation(sbinPath)) + + // This re-executes the exact same command passed to this program + binaryPath := os.Args[0] + var args []string + if len(os.Args) > 1 { + args = os.Args[1:] + } + + selector := iptables.BuildAlternativeSelector(sbinPath) + if err := selector.UseMode(ctx, mode); err != nil { + fmt.Fprintf(os.Stderr, "Unable to redirect iptables binaries. (Are you running in an unprivileged pod?): %s\n", err) + // fake it, though this will probably also fail if they aren't root + binaryPath = iptables.XtablesPath(sbinPath, mode) + args = os.Args + } + + cmdIPTables := exec.CommandContext(ctx, binaryPath, args...) + cmdIPTables.Stdout = os.Stdout + cmdIPTables.Stderr = os.Stderr + + if err := cmdIPTables.Run(); err != nil { + code := 1 + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + code = exitErr.ExitCode() + } else { + // If it's not an ExitError, the command probably didn't finish and something + // else failed, which means it might not had outputted anything. In that case, + // print the error message just in case. + fmt.Fprintf(os.Stderr, "Error: %s\n", err) + } + os.Exit(code) + } +} diff --git a/eks-distro-base/iptables-wrappers/test/Dockerfile.test-alpine b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-alpine new file mode 100644 index 000000000..b7cc73404 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-alpine @@ -0,0 +1,23 @@ +# Copyright 2020 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. + +### Dockerfile for building an Alpine image for testing + +FROM alpine:3.15 + +RUN apk add --no-cache iptables +COPY iptables-wrapper-installer.sh / +COPY bin/iptables-wrapper / +RUN /iptables-wrapper-installer.sh +COPY bin/tests / diff --git a/eks-distro-base/iptables-wrappers/test/Dockerfile.test-debian b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-debian new file mode 100644 index 000000000..ad86f8e11 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-debian @@ -0,0 +1,29 @@ +# Copyright 2020 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. + +### Dockerfile for building a Debian buster image for testing + +FROM debian:buster + +ARG INSTALL_ARGS= +ARG REPO=buster + +RUN echo deb http://deb.debian.org/debian buster-backports main >> /etc/apt/sources.list; \ + apt-get update; \ + apt-get -t ${REPO} -y --no-install-recommends install iptables + +COPY iptables-wrapper-installer.sh / +COPY bin/iptables-wrapper / +RUN /iptables-wrapper-installer.sh ${INSTALL_ARGS} +COPY bin/tests / diff --git a/eks-distro-base/iptables-wrappers/test/Dockerfile.test-distroless b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-distroless new file mode 100644 index 000000000..bd3ffecc2 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-distroless @@ -0,0 +1,60 @@ +# Copyright 2023 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. + +### Dockerfile for building a distroless image for testing +# Modified version of https://github.com/kubernetes/release/blob/master/images/build/distroless-iptables/distroless/Dockerfile + +ARG STAGE_DIR="/opt/stage" + +FROM debian:bullseye-slim as build +ARG STAGE_DIR + +COPY test/build/stage-binaries-from-package.sh / +COPY test/build/package-utils.sh / +COPY test/build/stage-binary-and-deps.sh / + +RUN mkdir -p "${STAGE_DIR}" && \ + /stage-binaries-from-package.sh "${STAGE_DIR}" conntrack \ + ebtables \ + ipset \ + iptables \ + kmod && \ + `# below binaries and dash are used by iptables-wrapper-installer.sh` \ + /stage-binary-and-deps.sh "${STAGE_DIR}" /bin/dash \ + /bin/mv \ + /bin/chmod \ + /bin/grep \ + /bin/ln \ + /bin/rm \ + /bin/sleep \ + /usr/bin/wc + +RUN ln -sf /bin/dash "${STAGE_DIR}"/bin/sh + +FROM gcr.io/distroless/static as intermediate +ARG STAGE_DIR + +COPY test/build/clean-distroless.sh /clean-distroless.sh +COPY --from=build "${STAGE_DIR}" / +COPY iptables-wrapper-installer.sh / +COPY bin/iptables-wrapper / +# iptables-wrapper-installer needs to know that iptables exists before doing all its magic +RUN echo "" > /usr/sbin/iptables && \ + /iptables-wrapper-installer.sh && \ + /clean-distroless.sh + +FROM scratch + +COPY --from=intermediate / / +COPY bin/tests / diff --git a/eks-distro-base/iptables-wrappers/test/Dockerfile.test-fedora b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-fedora new file mode 100644 index 000000000..e00b44d9a --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/Dockerfile.test-fedora @@ -0,0 +1,23 @@ +# Copyright 2020 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. + +### Dockerfile for building a Fedora 35 image for testing + +FROM fedora:35 + +RUN dnf install -y iptables iptables-legacy iptables-nft +COPY iptables-wrapper-installer.sh / +COPY bin/iptables-wrapper / +RUN /iptables-wrapper-installer.sh +COPY bin/tests / diff --git a/eks-distro-base/iptables-wrappers/test/build/clean-distroless.sh b/eks-distro-base/iptables-wrappers/test/build/clean-distroless.sh new file mode 100755 index 000000000..b46844c56 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/build/clean-distroless.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# 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. + +# USAGE: clean-distroless.sh + +# Modified version of https://github.com/kubernetes/release/blob/master/images/build/distroless-iptables/distroless/clean-distroless.sh + +REMOVE="/usr/share/base-files +/usr/share/man +/usr/lib/*-linux-gnu/gconv/ +/usr/bin/c_rehash +/usr/bin/openssl +/bin/mv +/bin/chmod +/bin/grep +/bin/ln +/bin/sleep +/usr/bin/wc +/iptables-wrapper-installer.sh +/bin/sh +/bin/dash +/clean-distroless.sh +/bin/rm" + +IFS=" +" + +for item in ${REMOVE}; do + rm -rf "${item}" +done diff --git a/eks-distro-base/iptables-wrappers/test/build/package-utils.sh b/eks-distro-base/iptables-wrappers/test/build/package-utils.sh new file mode 100644 index 000000000..e87cfd908 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/build/package-utils.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copied from https://github.com/kubernetes/release/blob/master/images/build/distroless-iptables/distroless/package-utils.sh + +# 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. + +# file_to_package identifies the debian package that provided the file $1 +file_to_package() { + # `dpkg-query --search $file-pattern` outputs lines with the format: "$package: $file-path" + # where $file-path belongs to $package + # https://manpages.debian.org/jessie/dpkg/dpkg-query.1.en.html + dpkg-query --search "$(realpath "${1}")" | cut -d':' -f1 +} + +# package_to_copyright gives the path to the copyright file for the package $1 +package_to_copyright() { + echo "/usr/share/doc/${1}/copyright" +} + +# stage_file stages the filepath $1 to $2, following symlinks +# and staging copyrights +stage_file() { + cp -a --parents "${1}" "${2}" + # recursively follow symlinks + if [[ -L "${1}" ]]; then + stage_file "$(cd "$(dirname "${1}")" || exit; realpath -s "$(readlink "${1}")")" "${2}" + fi + # get the package so we can stage package metadata as well + package="$(file_to_package "${1}")" + # stage the copyright for the file + cp -a --parents "$(package_to_copyright "${package}")" "${2}" + # stage the package status mimicking bazel + # https://github.com/bazelbuild/rules_docker/commit/f5432b813e0a11491cf2bf83ff1a923706b36420 + # instead of parsing the control file, we can just get the actual package status with dpkg + dpkg -s "${package}" > "${2}/var/lib/dpkg/status.d/${package}" +} diff --git a/eks-distro-base/iptables-wrappers/test/build/stage-binaries-from-package.sh b/eks-distro-base/iptables-wrappers/test/build/stage-binaries-from-package.sh new file mode 100755 index 000000000..b08bbaaa5 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/build/stage-binaries-from-package.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Copied from https://github.com/kubernetes/release/blob/master/images/build/distroless-iptables/distroless/stage-binaries-from-package.sh + +# 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. + +# USAGE: stage-binaries-from-package.sh /opt/stage package1 package2 +# +# Stages all the packages and its dependencies (+ libraries and copyrights) to $1 +# +# This is intended to be used in a multi-stage docker build with a distroless/base +# or distroless/cc image. +set -e + +. package-utils.sh + +stage_file_list() { + IFS=" + " + REQUIRED_FILES="$(dpkg -L "${1}" | grep -vE '(/\.|/s?bin/|/usr/share/(man|doc|.*-completion))' | sed 's/\n/ /g')" + for file in $REQUIRED_FILES; do + if [ -f "$file" ]; then + stage_file "${file}" "${STAGE_DIR}" + fi + done + + BIN_LIST="$(dpkg -L "${1}" | grep -E '/s?bin/' |sed 's/\n/ /g')" + for binary in $BIN_LIST; do + /stage-binary-and-deps.sh "${2}" "${binary}" + done +} + +get_dependent_packages() { + apt-cache depends "${1}" |grep Depends|awk -F '.*Depends:[[:space:]]?' '{print $2}' +} + +main() { + STAGE_DIR="${1}/" + mkdir -p "${STAGE_DIR}"/var/lib/dpkg/status.d/ + apt -y update + shift + while (( "$#" )); do # While there are arguments still to be shifted + PACKAGE="${1}" + apt -y install "${PACKAGE}" + stage_file_list "${PACKAGE}" "$STAGE_DIR" + while IFS= read -r c_dep; do + stage_file_list "${c_dep}" "${STAGE_DIR}" + done < <(get_dependent_packages "${PACKAGE}") + shift + done +} + +main "$@" diff --git a/eks-distro-base/iptables-wrappers/test/build/stage-binary-and-deps.sh b/eks-distro-base/iptables-wrappers/test/build/stage-binary-and-deps.sh new file mode 100755 index 000000000..c88ecc787 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/build/stage-binary-and-deps.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# Copied from https://github.com/kubernetes/release/blob/master/images/build/distroless-iptables/distroless/stage-binary-and-deps.sh + +# Copyright 2021 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. + +# USAGE: stage-binary-and-deps.sh haproxy /opt/stage +# +# Stages $1 and its dependencies + their copyright files to $2 +# +# This is intended to be used in a multi-stage docker build with a distroless/base +# or distroless/cc image. +# This script was originally created by KinD maintainers and can be found at: +# https://github.com/kubernetes-sigs/kind/blob/v0.14.0/images/haproxy/stage-binary-and-deps.sh + +set -o errexit +set -o nounset +set -o pipefail + + +. package-utils.sh + +# binary_to_libraries identifies the library files needed by the binary $1 with ldd +binary_to_libraries() { + # see: https://man7.org/linux/man-pages/man1/ldd.1.html + ldd "${1}" \ + `# strip the leading '${name} => ' if any so only '/lib-foo.so (0xf00)' remains` \ + | sed -E 's#.* => /#/#' \ + `# we want only the path remaining, not the (0x${LOCATION})` \ + | awk '{print $1}' \ + `# linux-vdso.so.1 is a special virtual shared object from the kernel` \ + `# see: http://man7.org/linux/man-pages/man7/vdso.7.html` \ + | grep -v 'linux-vdso.so.1' +} + +# main script logic +main(){ + local STAGE_DIR="${1}/" + shift + while (( "$#" )); do + BINARY="${1}" + # locate the path to the binary + local binary_path + binary_path="$(which "${BINARY}")" + + # ensure package metadata dir + mkdir -p "${STAGE_DIR}"/var/lib/dpkg/status.d/ + + # stage the binary itself + stage_file "${binary_path}" "${STAGE_DIR}" + + # stage the dependencies of the binary + while IFS= read -r c_dep; do + stage_file "${c_dep}" "${STAGE_DIR}" + done < <(binary_to_libraries "${binary_path}") + shift + done +} + +main "$@" diff --git a/eks-distro-base/iptables-wrappers/test/run-test.sh b/eks-distro-base/iptables-wrappers/test/run-test.sh new file mode 100755 index 000000000..b08a6df1b --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/run-test.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# +# Copyright 2020 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. + +set -o errexit +set -o nounset +set -o pipefail + +if [[ -n "${DEBUG:-}" ]]; then + set -x +fi + +build_arg="" +build_fail=0 +nft_fail=0 + +while [[ $# -gt 1 ]]; do + case "$1" in + --build-arg=*) + build_arg="${1#--build_arg=}" + ;; + --build-arg) + shift + build_arg="$1" + ;; + --build-fail) + build_fail=1 + ;; + --nft-fail) + nft_fail=1 + ;; + *) + echo "Unrecognized flag '$1'" 1>&2 + exit 1 + ;; + esac + shift +done +tag="$1" + +if podman -h &> /dev/null; then + docker_binary=podman +elif docker -h &> /dev/null; then + if docker version &> /dev/null; then + docker_binary=docker + else + docker_binary="sudo docker" + # Get the password prompting out of the way now + sudo docker version > /dev/null + fi +else + echo "Could not find podman or docker" 1>&2 + exit 1 +fi + +function docker() { + if [[ -n "${DEBUG:-}" ]]; then + command ${docker_binary} "$@" + else + if [[ "$1" == "build" ]]; then + echo " docker $*" + fi + # Redirect stdout to /dev/null and indent stderr + command ${docker_binary} "$@" 2>&1 > /dev/null | \ + sed -e '/debconf: delaying package configuration/ d' \ + -e 's/^/ /' + fi +} + +function build() { + if [[ -z "${DEBUG:-}" ]]; then + quiet="-q" + fi + + if [[ -z "${CACHE_BUILDS:-}" ]]; then + no_cache="--no-cache" + fi + + build_tag=iptables-wrapper-test-$1 + dockerfile=Dockerfile.test-${1%%-*} + shift + + docker build ${no_cache:-} ${quiet:-} -t ${build_tag} -f test/${dockerfile} "$@" . +} + +function PASS() { + printf "\033[1;92mPASS: $@\033[0m\n\n" + exit 0 +} + +function FAIL() { + printf "\033[1;31mFAIL: $@\033[0m\n" 1>&2 + exit 1 +} + +if ! build "${tag}" ${build_arg}; then + if [[ "${build_fail}" = 1 ]]; then + PASS "build failed as expected" + fi + FAIL "build failed unexpectedly" +fi + +if ! docker run --privileged "iptables-wrapper-test-${tag}" /tests -test.v -test.run "^TestIPTablesWrapperLegacy$" ; then + FAIL "failed legacy iptables" +fi +if ! docker run --privileged "iptables-wrapper-test-${tag}" /tests -test.v -test.run "^TestIPTablesWrapperNFT$" ; then + FAIL "failed nft iptables" +fi + +if ! docker run --privileged "iptables-wrapper-test-${tag}" /tests -test.v -test.run "^TestIP6TablesWrapperLegacy$" ; then + FAIL "failed legacy ip6tables" +fi +if ! docker run --privileged "iptables-wrapper-test-${tag}" /tests -test.v -test.run "^TestIP6TablesWrapperNFT$" ; then + FAIL "failed nft ip6tables" +fi + +PASS "success" diff --git a/eks-distro-base/iptables-wrappers/test/wrapper_test.go b/eks-distro-base/iptables-wrappers/test/wrapper_test.go new file mode 100644 index 000000000..2af20f1d1 --- /dev/null +++ b/eks-distro-base/iptables-wrappers/test/wrapper_test.go @@ -0,0 +1,229 @@ +/* +Copyright 2023 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 test + +import ( + "bytes" + "context" + "os/exec" + "path/filepath" + "regexp" + "testing" + + "github.com/kubernetes-sigs/iptables-wrappers/internal/commands" + "github.com/kubernetes-sigs/iptables-wrappers/internal/iptables" +) + +// iptablesVersion denotes the IP version for a iptables command, V4 or V6 +type iptablesIPVersion string + +const ( + v4 iptablesIPVersion = "iptables" + v6 iptablesIPVersion = "ip6tables" +) + +// iptablesMode represents a iptables mode. +type iptablesMode struct { + original, wrongMode iptables.Mode + // expectedIPTablesVStr is the subtring expected in betwen brakets when + // running `iptables -V` for this particular mode + // ex. for nft -> `iptables v1.8.7 (nf_tables)` + expectedIPTablesVStr string +} + +var legacy = iptablesMode{ + original: iptables.Legacy, + wrongMode: iptables.NFT, + expectedIPTablesVStr: "legacy", +} + +var nft = iptablesMode{ + original: iptables.NFT, + wrongMode: iptables.Legacy, + expectedIPTablesVStr: "nf_tables", +} + +func TestIPTablesWrapperLegacy(t *testing.T) { + tt := newIPTablesWrapperTest(t, v4, legacy) + runTest(t, tt) +} + +func TestIPTablesWrapperNFT(t *testing.T) { + tt := newIPTablesWrapperTest(t, v4, nft) + runTest(t, tt) +} + +func TestIP6TablesWrapperLegacy(t *testing.T) { + tt := newIPTablesWrapperTest(t, v6, legacy) + runTest(t, tt) +} + +func TestIP6TablesWrapperNFT(t *testing.T) { + tt := newIPTablesWrapperTest(t, v6, nft) + runTest(t, tt) +} + +func runTest(tb testing.TB, test iptablesWrapperTest) { + ctx := context.Background() + test.assertIPTablesUndecided(tb) + + tb.Log("Inserting chains") + // Initialize the chosen iptables mode with just a hint chain + test.iptables.runAndAssertSuccess(ctx, tb, "-t", "mangle", "-N", "KUBE-IPTABLES-HINT") + + // Put some junk in the other iptables system + test.wrongModeIPTables.runAndAssertSuccess(ctx, tb, "-t", "filter", "-N", "BAD-1") + test.wrongModeIPTables.runAndAssertSuccess(ctx, tb, "-t", "filter", "-A", "BAD-1", "-j", "ACCEPT") + test.wrongModeIPTables.runAndAssertSuccess(ctx, tb, "-t", "filter", "-N", "BAD-2") + test.wrongModeIPTables.runAndAssertSuccess(ctx, tb, "-t", "filter", "-A", "BAD-2", "-j", "DROP") + + test.assertIPTablesUndecided(tb) + + // This should run the iptables-wrapper + tb.Log("Running `iptables -L` command") + c := exec.CommandContext(ctx, "iptables", "-L") + assertSuccess(tb, commands.RunAndReadError(c)) + + test.assertIPTablesResolved(ctx, tb) +} + +type iptablesWrapperTest struct { + mode iptablesMode + iptables, wrongModeIPTables ipTablesRunner + sbinPath string + wrapperPath string + iptablesPath, ip6tablesPath string +} + +// newIPTablesWrapperTest creates a new test setup for a particular IP version of iptables (iptables or ip6tables) +// and a particular mode (legacy or nft) +func newIPTablesWrapperTest(tb testing.TB, ipV iptablesIPVersion, mode iptablesMode) iptablesWrapperTest { + sbinPath, err := iptables.DetectBinaryDir() + assertSuccess(tb, err) + + return iptablesWrapperTest{ + mode: mode, + iptables: newIPTablesRunner(ipV, mode.original), + wrongModeIPTables: newIPTablesRunner(ipV, mode.wrongMode), + sbinPath: sbinPath, + wrapperPath: filepath.Join(sbinPath, "iptables-wrapper"), + iptablesPath: filepath.Join(sbinPath, "iptables"), + ip6tablesPath: filepath.Join(sbinPath, "ip6tables"), + } +} + +func (tt iptablesWrapperTest) assertIPTablesUndecided(tb testing.TB) { + tb.Log("Checking the iptables mode hasn't been decided yet") + iptablesRealPath := tt.iptablesRealPath(tb) + if !tt.isIPTablesWrapper(iptablesRealPath) { + tb.Fatalf("iptables link was resolved prematurely, got [%s]", iptablesRealPath) + } + tb.Logf("iptables points to %s", iptablesRealPath) + + ip6tablesRealPath := tt.ip6tablesRealPath(tb) + if !tt.isIPTablesWrapper(ip6tablesRealPath) { + tb.Fatalf("ip6tables link was resolved prematurely, got [%s]", ip6tablesRealPath) + } + tb.Logf("ip6tables points to %s", ip6tablesRealPath) +} + +func (tt iptablesWrapperTest) assertIPTablesResolved(ctx context.Context, tb testing.TB) { + tb.Logf("Checking the iptables mode has been resolved to %s", tt.mode.original) + iptablesRealPath := tt.iptablesRealPath(tb) + if tt.isIPTablesWrapper(iptablesRealPath) { + tb.Fatal("iptables link is not yet resolved") + } + + ip6tablesRealPath := tt.iptablesRealPath(tb) + if tt.isIPTablesWrapper(ip6tablesRealPath) { + tb.Fatal("ip6tables link is not yet resolved") + } + + mode := readIPTablesMode(ctx, tb, "iptables") + if mode != tt.mode.expectedIPTablesVStr { + tb.Fatalf("iptables link resolved incorrectly: expected %s, got %s", tt.mode.expectedIPTablesVStr, mode) + } + + mode = readIPTablesMode(ctx, tb, "ip6tables") + if mode != tt.mode.expectedIPTablesVStr { + tb.Fatalf("ip6tables link resolved incorrectly: expected %s, got %s", tt.mode.expectedIPTablesVStr, mode) + } +} + +func (tt iptablesWrapperTest) isIPTablesWrapper(binaryRealPath string) bool { + return binaryRealPath == tt.wrapperPath +} + +func (tt iptablesWrapperTest) iptablesRealPath(tb testing.TB) string { + return binaryRealPath(tb, tt.iptablesPath) +} + +func (tt iptablesWrapperTest) ip6tablesRealPath(tb testing.TB) string { + return binaryRealPath(tb, tt.ip6tablesPath) +} + +func binaryRealPath(tb testing.TB, binary string) string { + realPath, err := filepath.EvalSymlinks(binary) + assertSuccess(tb, err) + + return realPath +} + +func newIPTablesRunner(ipV iptablesIPVersion, mode iptables.Mode) ipTablesRunner { + return ipTablesRunner{ + binary: string(ipV) + "-" + string(mode), + } +} + +type ipTablesRunner struct { + binary string +} + +func (r ipTablesRunner) runAndAssertSuccess(ctx context.Context, tb testing.TB, args ...string) { + tb.Helper() + assertSuccess(tb, r.run(ctx, args...)) +} + +func (r ipTablesRunner) run(ctx context.Context, args ...string) error { + c := exec.CommandContext(ctx, r.binary, args...) + return commands.RunAndReadError(c) +} + +var iptablesModeRegex = regexp.MustCompile(`^ip6?tables.*\((.+)\).*`) + +func readIPTablesMode(ctx context.Context, tb testing.TB, iptables string) string { + tb.Helper() + var out bytes.Buffer + c := exec.CommandContext(ctx, iptables, "-V") + c.Stdout = &out + assertSuccess(tb, commands.RunAndReadError(c)) + + outIPTablesVersion := out.String() + matches := iptablesModeRegex.FindStringSubmatch(outIPTablesVersion) + if len(matches) != 2 { + tb.Fatalf("Can't read `%s -V` output format: %s", iptables, outIPTablesVersion) + } + + tb.Logf("Output of `%s -V`: %s", iptables, outIPTablesVersion) + + mode := matches[1] + return mode +} + +func assertSuccess(tb testing.TB, err error) { + tb.Helper() + if err != nil { + tb.Fatal(err.Error()) + } +} diff --git a/eks-distro-base/tests/Dockerfile b/eks-distro-base/tests/Dockerfile index c2da587ae..983239bf5 100644 --- a/eks-distro-base/tests/Dockerfile +++ b/eks-distro-base/tests/Dockerfile @@ -1,5 +1,6 @@ ARG BASE_IMAGE ARG AL_TAG +ARG BUILDER_IMAGE=public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base:${AL_TAG} FROM ${BASE_IMAGE} as base FROM public.ecr.aws/eks-distro-build-tooling/golang:1.18-gcc-al${AL_TAG} as builder @@ -36,6 +37,19 @@ RUN ["update-alternatives", "--set", "ip6tables", "/usr/sbin/ip6tables-nft"] CMD ["iptables"] +FROM public.ecr.aws/eks-distro-build-tooling/golang:1.20-al${AL_TAG} as iptables-wrapper-tests-builder + +COPY iptables-wrappers /iptables-wrappers +RUN cd /iptables-wrappers && \ + make build-tests + +FROM ${BASE_IMAGE} as check-iptables-wrapper + +COPY --from=iptables-wrapper-tests-builder /iptables-wrappers/bin/tests / +RUN ["update-alternatives", "--set", "iptables", "/usr/sbin/iptables-wrapper"] + +CMD ["iptables"] + FROM builder as git-builder ARG GOPROXY diff --git a/eks-distro-base/tests/run_tests.sh b/eks-distro-base/tests/run_tests.sh index 7903f7049..b4ca32bcc 100755 --- a/eks-distro-base/tests/run_tests.sh +++ b/eks-distro-base/tests/run_tests.sh @@ -103,6 +103,17 @@ function check_base-glibc() { done } +function cleanup-iptable-rules() { + local mode + local ip + for mode in legacy nft; do + # ensure they do not already exist + for ip in iptables ip6tables; do + docker run --privileged --rm --net host --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-$mode-latest $ip -t mangle -X KUBE-IPTABLES-HINT &> /dev/null || true + done + done +} + function check_base-iptables() { $SCRIPT_ROOT/../../scripts/buildkit.sh \ build \ @@ -134,9 +145,38 @@ function check_base-iptables() { --import-cache type=registry,ref=$LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest \ --output type=image,oci-mediatypes=true,\"name=$LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest\",push=true + $SCRIPT_ROOT/../../scripts/buildkit.sh \ + build \ + --frontend dockerfile.v0 \ + --opt filename=./tests/Dockerfile \ + --opt platform=$PLATFORMS \ + --opt build-arg:BASE_IMAGE=$IMAGE_REPO/eks-distro-minimal-base-iptables:$IMAGE_TAG \ + --opt build-arg:AL_TAG=$AL_TAG \ + --progress plain \ + --opt target=check-iptables-wrapper \ + --local dockerfile=./ \ + --local context=$SCRIPT_ROOT/../ \ + --export-cache type=inline \ + --import-cache type=registry,ref=$LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest \ + --output type=image,oci-mediatypes=true,\"name=$LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest\",push=true + + local platform + local mode + local ip for platform in ${PLATFORMS//,/ }; do build::docker::retry_pull --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-legacy-latest + # ensure defatult is set to legacy which was set in dockerfile + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-legacy-latest update-alternatives --list | grep 'iptables manual /usr/sbin/iptables-legacy'; then + echo "iptables legacy alternative issue!" + exit 1 + fi + + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-legacy-latest update-alternatives --list | grep 'ip6tables manual /usr/sbin/ip6tables-legacy'; then + echo "ip6tables legacy alternative issue!" + exit 1 + fi + if docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-legacy-latest iptables --version | grep -v 'legacy'; then echo "iptables legacy issue!" exit 1 @@ -146,6 +186,7 @@ function check_base-iptables() { echo "ip6tables legacy issue!" exit 1 fi + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-legacy-latest iptables-save; then echo "iptables-save legacy issue!" exit 1 @@ -163,22 +204,111 @@ function check_base-iptables() { build::docker::retry_pull --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest + # ensure defatult is set to nft which was set in dockerfile + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest update-alternatives --list | grep 'iptables manual /usr/sbin/iptables-nft'; then + echo "iptables nft alternative issue!" + exit 1 + fi + + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest update-alternatives --list | grep 'ip6tables manual /usr/sbin/ip6tables-nft'; then + echo "ip6tables nft alternative issue!" + exit 1 + fi + if docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest iptables --version | grep -v 'nf_tables'; then echo "iptables nft issue!" exit 1 fi + if docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest ip6tables --version | grep -v 'nf_tables'; then echo "ip6tables nft issue!" exit 1 fi + + # these are included to match upstream, but default to nft and are not explictly set anywhere accross eks-d/a if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest ebtables --version; then echo "ebtables nft issue!" exit 1 fi + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-nft-latest arptables --version; then echo "arptables nft issue!" exit 1 fi + + + build::docker::retry_pull --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest + + # ensure defatult is set to wrapper which was set in dockerfile + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest update-alternatives --list | grep 'iptables manual /usr/sbin/iptables-wrapper'; then + echo "iptables wrapper alternative issue!" + exit 1 + fi + + if ! docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest update-alternatives --list | grep 'ip6tables manual /usr/sbin/iptables-wrapper'; then + echo "ip6tables wrapper alternative issue!" + exit 1 + fi + + # iptables-wrapper defaults to nft mode when none of the kubelet hint rules are set + if docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest iptables --version | grep -v 'nf_tables'; then + echo "iptables wrapper issue!" + exit 1 + fi + + if docker run --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest ip6tables --version | grep -v 'nf_tables'; then + echo "ip6tables wrapper issue!" + exit 1 + fi + + # create KUBE-IPTABLES-HINT and test that wrapper sets the correct mode + for mode in nft legacy; do + local expected="$mode" + if [[ "$mode" == "nft" ]]; then + expected="nf_tables" + fi + + # no matter which version of a given iptables mode is used to add the hint, it should trigger the wrapper to use that mode for both + # depending on how the hint rule is set, the resulting mode should be set + for ip in iptables ip6tables; do + cleanup-iptable-rules + + docker run --privileged --rm --net host --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-$mode-latest $ip -t mangle -N KUBE-IPTABLES-HINT + + if docker run --privileged --net host --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest iptables --version | grep -v "$expected"; then + echo "iptables wrapper issue!" + exit 1 + fi + + if docker run --privileged --net host --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest ip6tables --version | grep -v "$expected"; then + echo "iptables wrapper issue!" + exit 1 + fi + done + done + + cleanup-iptable-rules + + # run upstream test binaries which tests a number of similiar cases as above + if ! docker run --privileged --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest /tests -test.v -test.run "^TestIPTablesWrapperLegacy$"; then + echo "iptables wrapper legacy issue!" + exit 1 + fi + + if ! docker run --privileged --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest /tests -test.v -test.run "^TestIPTablesWrapperNFT$"; then + echo "iptables wrapper nft issue!" + exit 1 + fi + + if ! docker run --privileged --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest /tests -test.v -test.run "^TestIP6TablesWrapperLegacy$"; then + echo "iptables wrapper ipv6 legacy issue!" + exit 1 + fi + + if ! docker run --privileged --rm --platform=$platform $LOCAL_REGISTRY/eks-distro-minimal-images-base-test:iptables-wrapper-latest /tests -test.v -test.run "^TestIP6TablesWrapperNFT$"; then + echo "ip6tables wrapper ipv6 nft issue!" + exit 1 + fi done }