Always use iptables -C to look for rules

iptables -C flag was introduced in v1.4.11, which was released ten
years ago. Thus, there're no more Linux distributions supported by
Docker using this version. As such, this commit removes the old way of
checking if an iptables rule exists (by using substring matching).

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
Albin Kerouanton 2021-12-06 02:23:42 +01:00 committed by Albin Kerouanton
parent 205e5278c6
commit 799cc143c9
No known key found for this signature in database
GPG key ID: 630B8E1DCBDB1864
2 changed files with 7 additions and 82 deletions

View file

@ -8,7 +8,6 @@ import (
"fmt"
"net"
"os/exec"
"regexp"
"strconv"
"strings"
"sync"
@ -56,7 +55,6 @@ var (
iptablesPath string
ip6tablesPath string
supportsXlock = false
supportsCOpt = false
xLockWaitMsg = "Another app is currently holding the xtables lock"
// used to lock iptables commands if xtables lock is not supported
bestEffortLock sync.Mutex
@ -96,19 +94,14 @@ func detectIptables() {
}
iptablesPath = path
// The --wait flag was added in iptables v1.6.0.
// TODO remove this check once we drop support for CentOS/RHEL 7, which uses an older version of iptables
if out, err := exec.Command(path, "--wait", "-L", "-n").CombinedOutput(); err != nil {
logrus.WithError(err).Infof("unable to detect if iptables supports xlock: 'iptables --wait -L -n': `%s`", strings.TrimSpace(string(out)))
} else {
supportsXlock = true
}
mj, mn, mc, err := GetVersion()
if err != nil {
logrus.Warnf("Failed to read iptables version: %v", err)
} else {
supportsCOpt = supportsCOption(mj, mn, mc)
}
path, err = exec.LookPath("ip6tables")
if err != nil {
logrus.WithError(err).Warnf("unable to find ip6tables")
@ -463,26 +456,9 @@ func (iptable IPTable) exists(native bool, table Table, chain string, rule ...st
return false
}
if supportsCOpt {
// if exit status is 0 then return true, the rule exists
_, err := f(append([]string{"-t", string(table), "-C", chain}, rule...)...)
return err == nil
}
// parse "iptables -S" for the rule (it checks rules in a specific chain
// in a specific table and it is very unreliable)
return iptable.existsRaw(table, chain, rule...)
}
func (iptable IPTable) existsRaw(table Table, chain string, rule ...string) bool {
path := iptablesPath
if iptable.Version == IPv6 {
path = ip6tablesPath
}
ruleString := fmt.Sprintf("%s %s\n", chain, strings.Join(rule, " "))
existingRules, _ := exec.Command(path, "-t", string(table), "-S", chain).Output()
return strings.Contains(string(existingRules), ruleString)
// if exit status is 0 then return true, the rule exists
_, err := f(append([]string{"-t", string(table), "-C", chain}, rule...)...)
return err == nil
}
// Maximum duration that an iptables operation can take
@ -580,15 +556,6 @@ func (iptable IPTable) ExistChain(chain string, table Table) bool {
return false
}
// GetVersion reads the iptables version numbers during initialization
func GetVersion() (major, minor, micro int, err error) {
out, err := exec.Command(iptablesPath, "--version").CombinedOutput()
if err == nil {
major, minor, micro = parseVersionNumbers(string(out))
}
return
}
// SetDefaultPolicy sets the passed default policy for the table/chain
func (iptable IPTable) SetDefaultPolicy(table Table, chain string, policy Policy) error {
if err := iptable.RawCombinedOutput("-t", string(table), "-P", chain, string(policy)); err != nil {
@ -597,19 +564,6 @@ func (iptable IPTable) SetDefaultPolicy(table Table, chain string, policy Policy
return nil
}
func parseVersionNumbers(input string) (major, minor, micro int) {
re := regexp.MustCompile(`v\d*.\d*.\d*`)
line := re.FindString(input)
fmt.Sscanf(line, "v%d.%d.%d", &major, &minor, &micro)
return
}
// iptables -C, --check option was added in v.1.4.11
// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
func supportsCOption(mj, mn, mc int) bool {
return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11)))
}
// AddReturnRule adds a return rule for the chain in the filter table
func (iptable IPTable) AddReturnRule(chain string) error {
var (

View file

@ -284,44 +284,15 @@ func TestExistsRaw(t *testing.T) {
if err != nil {
t.Fatalf("i=%d, err: %v", i, err)
}
if !iptable.existsRaw(Filter, testChain1, r.rule...) {
if !iptable.exists(true, Filter, testChain1, r.rule...) {
t.Fatalf("Failed to detect rule. i=%d", i)
}
// Truncate the rule
trg := r.rule[len(r.rule)-1]
trg = trg[:len(trg)-2]
r.rule[len(r.rule)-1] = trg
if iptable.existsRaw(Filter, testChain1, r.rule...) {
if iptable.exists(true, Filter, testChain1, r.rule...) {
t.Fatalf("Invalid detection. i=%d", i)
}
}
}
func TestGetVersion(t *testing.T) {
mj, mn, mc := parseVersionNumbers("iptables v1.4.19.1-alpha")
if mj != 1 || mn != 4 || mc != 19 {
t.Fatal("Failed to parse version numbers")
}
}
func TestSupportsCOption(t *testing.T) {
input := []struct {
mj int
mn int
mc int
ok bool
}{
{1, 4, 11, true},
{1, 4, 12, true},
{1, 5, 0, true},
{0, 4, 11, false},
{0, 5, 12, false},
{1, 3, 12, false},
{1, 4, 10, false},
}
for ind, inp := range input {
if inp.ok != supportsCOption(inp.mj, inp.mn, inp.mc) {
t.Fatalf("Incorrect check: %d", ind)
}
}
}