64 lines
1.8 KiB
Go
64 lines
1.8 KiB
Go
package firewall
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/qdm12/golibs/command"
|
|
)
|
|
|
|
var (
|
|
ErrNetAdminMissing = errors.New("NET_ADMIN capability is missing")
|
|
ErrTestRuleCleanup = errors.New("failed cleaning up test rule")
|
|
ErrIPTablesNotSupported = errors.New("no iptables supported found")
|
|
)
|
|
|
|
func checkIptablesSupport(ctx context.Context, runner command.Runner,
|
|
iptablesPathsToTry ...string) (iptablesPath string, err error) {
|
|
var errMessage string
|
|
testInterfaceName := randomInterfaceName()
|
|
for _, iptablesPath = range iptablesPathsToTry {
|
|
cmd := exec.CommandContext(ctx, iptablesPath, "-A", "INPUT", "-i", testInterfaceName, "-j", "DROP")
|
|
errMessage, err = runner.Run(cmd)
|
|
if err == nil {
|
|
break
|
|
}
|
|
|
|
const permissionDeniedString = "Permission denied (you must be root)"
|
|
if strings.Contains(errMessage, permissionDeniedString) {
|
|
return "", fmt.Errorf("%w: %s (%s)", ErrNetAdminMissing, errMessage, err)
|
|
}
|
|
errMessage = fmt.Sprintf("%s (%s)", errMessage, err)
|
|
}
|
|
|
|
if err != nil { // all iptables to try failed
|
|
return "", fmt.Errorf("%w: from %s: last error is: %s",
|
|
ErrIPTablesNotSupported, strings.Join(iptablesPathsToTry, ", "),
|
|
errMessage)
|
|
}
|
|
|
|
// Cleanup test rule
|
|
cmd := exec.CommandContext(ctx, iptablesPath, "-D", "INPUT", "-i", testInterfaceName, "-j", "DROP")
|
|
errMessage, err = runner.Run(cmd)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %s (%s)", ErrTestRuleCleanup, errMessage, err)
|
|
}
|
|
|
|
return iptablesPath, nil
|
|
}
|
|
|
|
func randomInterfaceName() (interfaceName string) {
|
|
const size = 15
|
|
letterRunes := []rune("abcdefghijklmnopqrstuvwxyz0123456789")
|
|
b := make([]rune, size)
|
|
for i := range b {
|
|
letterIndex := rand.Intn(len(letterRunes)) //nolint:gosec
|
|
b[i] = letterRunes[letterIndex]
|
|
}
|
|
return string(b)
|
|
}
|