/* 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) } }