coreos / go-iptables

Go wrapper around iptables utility
Apache License 2.0
1.11k stars 257 forks source link

[BUG] Prepending iptables path to list of args in `runWithOutput` makes some methods fail unexpectedly #106

Closed gianni4sec closed 1 year ago

gianni4sec commented 1 year ago

Description of the issue

It seems like in the function runWithOutput the iptables path is erroneously prepended to the list of args, resulting in some methods failing unexpectedly, as in the example below. I've only tested Exists so far. The solution is to simply remove the prepended iptables path, everything seems to work correctly this way.

https://github.com/coreos/go-iptables/blob/fa6abe8703246b05c2c632ac4260eddd42f38dbf/iptables/iptables.go#L521-L522

Then further down in the same function: https://github.com/coreos/go-iptables/blob/fa6abe8703246b05c2c632ac4260eddd42f38dbf/iptables/iptables.go#L544-L549

How to reproduce

Example:

package main

import (
    "bytes"
    "fmt"
    "os/exec"

    "github.com/coreos/go-iptables/iptables"
)

func main() {
    const setName = "some-unexisting-set"
    const filterTableName = "filter"

    ipv4, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
    if err != nil {
        panic(err)
    }

    fmt.Println("Using go-iptables:")
    _, err = ipv4.Exists(filterTableName, "INPUT", "-m set --match-set", setName, "src", "-j DROP")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println()
    fmt.Println("Using exec.Command without repetition:")
    var stdout, stderr bytes.Buffer
    cmd := exec.Command("/usr/sbin/iptables", "-t", filterTableName, "-C", "INPUT", "-m", "set", "--match-set", setName, "src", "-j", "DROP", "--wait")
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr
    if err := cmd.Run(); err != nil {
        fmt.Printf("running %v: exit status %v: %v", append([]string{"/usr/sbin/iptables"}, cmd.Args...), cmd.ProcessState.ExitCode(), stderr.String())
    }
}

Output:

Using go-iptables:
cmd /usr/sbin/iptables [/usr/sbin/iptables -t filter -C INPUT -m set --match-set some-unexisting-set src -j DROP --wait]
running [/usr/sbin/iptables -t filter -C INPUT -m set --match-set some-unexisting-set src -j DROP --wait]: exit status 2: iptables v1.8.4 (legacy): Couldn't load match ` set --match-set':No such file or directory

Try `iptables -h' or 'iptables --help' for more information.

Using exec.Command without repetition:
running [/usr/sbin/iptables /usr/sbin/iptables -t filter -C INPUT -m set --match-set some-unexisting-set src -j DROP --wait]: exit status 2: iptables v1.8.4 (legacy): Set some-unexisting-set doesn't exist.

Try `iptables -h' or 'iptables --help' for more information.

Please note "Couldn't load match ` set --match-set':No such file or directory" vs "Set some-unexisting-set doesn't exist." in the error.

bgilbert commented 1 year ago

Argument 0 to any Unix process is the name of the program being executed, but the kernel does not handle that automatically. So it's correct and conventional that we're specifying the program name twice: once as the file to be executed, and once as its argument 0.

The actual problem is that in the good case, you're specifying -m set --match-set as three different arguments, but in the bad case you're incorrectly specifying -m set --match-set as a single argument. This is indicated in the error message you received. (You're also doing the same with -j DROP.)

gianni4sec commented 1 year ago

Thanks for spotting this! Indeed that is the problem.