1. Try setting a dummy output rule 2. Remove the dummy output rule 3. Get the INPUT table policy 4. Set the INPUT table policy to its existing policy
251 lines
7.3 KiB
Go
251 lines
7.3 KiB
Go
package firewall
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/qdm12/golibs/command"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
//go:generate mockgen -destination=runner_mock_test.go -package $GOPACKAGE github.com/qdm12/golibs/command Runner
|
|
|
|
func Test_testIptablesPath(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
const path = "dummypath"
|
|
errDummy := errors.New("exit code 4")
|
|
const inputPolicy = "ACCEPT"
|
|
|
|
appendTestRuleMatcher := newCmdMatcher(path,
|
|
"^-A$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
|
"^-j$", "^DROP$")
|
|
deleteTestRuleMatcher := newCmdMatcher(path,
|
|
"^-D$", "^OUTPUT$", "^-o$", "^[a-z0-9]{15}$",
|
|
"^-j$", "^DROP$")
|
|
listInputRulesMatcher := newCmdMatcher(path,
|
|
"^-L$", "^INPUT$")
|
|
setPolicyMatcher := newCmdMatcher(path,
|
|
"^--policy$", "^INPUT$", "^"+inputPolicy+"$")
|
|
|
|
testCases := map[string]struct {
|
|
buildRunner func(ctrl *gomock.Controller) command.Runner
|
|
ok bool
|
|
unsupportedMessage string
|
|
criticalErrWrapped error
|
|
criticalErrMessage string
|
|
}{
|
|
"append test rule permission denied": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).
|
|
Return("Permission denied (you must be root)", errDummy)
|
|
return runner
|
|
},
|
|
criticalErrWrapped: ErrNetAdminMissing,
|
|
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
|
"Permission denied (you must be root)",
|
|
},
|
|
"append test rule unsupported": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).
|
|
Return("some output", errDummy)
|
|
return runner
|
|
},
|
|
unsupportedMessage: "some output (exit code 4)",
|
|
},
|
|
"remove test rule error": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).
|
|
Return("some output", errDummy)
|
|
return runner
|
|
},
|
|
criticalErrWrapped: ErrTestRuleCleanup,
|
|
criticalErrMessage: "failed cleaning up test rule: some output (exit code 4)",
|
|
},
|
|
"list input rules permission denied": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(listInputRulesMatcher).
|
|
Return("Permission denied (you must be root)", errDummy)
|
|
return runner
|
|
},
|
|
criticalErrWrapped: ErrNetAdminMissing,
|
|
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
|
"Permission denied (you must be root)",
|
|
},
|
|
"list input rules unsupported": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(listInputRulesMatcher).
|
|
Return("some output", errDummy)
|
|
return runner
|
|
},
|
|
unsupportedMessage: "some output (exit code 4)",
|
|
},
|
|
"list input rules no policy": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(listInputRulesMatcher).
|
|
Return("some\noutput", nil)
|
|
return runner
|
|
},
|
|
criticalErrWrapped: ErrInputPolicyNotFound,
|
|
criticalErrMessage: "input policy not found: in INPUT rules: some\noutput",
|
|
},
|
|
"set policy permission denied": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(listInputRulesMatcher).
|
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
|
runner.EXPECT().Run(setPolicyMatcher).
|
|
Return("Permission denied (you must be root)", errDummy)
|
|
return runner
|
|
},
|
|
criticalErrWrapped: ErrNetAdminMissing,
|
|
criticalErrMessage: "NET_ADMIN capability is missing: " +
|
|
"Permission denied (you must be root)",
|
|
},
|
|
"set policy unsupported": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(listInputRulesMatcher).
|
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
|
runner.EXPECT().Run(setPolicyMatcher).
|
|
Return("some output", errDummy)
|
|
return runner
|
|
},
|
|
unsupportedMessage: "some output (exit code 4)",
|
|
},
|
|
"success": {
|
|
buildRunner: func(ctrl *gomock.Controller) command.Runner {
|
|
runner := NewMockRunner(ctrl)
|
|
runner.EXPECT().Run(appendTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(deleteTestRuleMatcher).Return("", nil)
|
|
runner.EXPECT().Run(listInputRulesMatcher).
|
|
Return("\nChain INPUT (policy "+inputPolicy+")\nxx\n", nil)
|
|
runner.EXPECT().Run(setPolicyMatcher).Return("some output", nil)
|
|
return runner
|
|
},
|
|
ok: true,
|
|
},
|
|
}
|
|
|
|
for name, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
ctrl := gomock.NewController(t)
|
|
|
|
runner := testCase.buildRunner(ctrl)
|
|
|
|
ok, unsupportedMessage, criticalErr :=
|
|
testIptablesPath(ctx, path, runner)
|
|
|
|
assert.Equal(t, testCase.ok, ok)
|
|
assert.Equal(t, testCase.unsupportedMessage, unsupportedMessage)
|
|
assert.ErrorIs(t, criticalErr, testCase.criticalErrWrapped)
|
|
if testCase.criticalErrWrapped != nil {
|
|
assert.EqualError(t, criticalErr, testCase.criticalErrMessage)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_isPermissionDenied(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := map[string]struct {
|
|
errMessage string
|
|
ok bool
|
|
}{
|
|
"empty error": {},
|
|
"other error": {
|
|
errMessage: "some error",
|
|
},
|
|
"permission denied": {
|
|
errMessage: "Permission denied (you must be root) have you tried blabla",
|
|
ok: true,
|
|
},
|
|
}
|
|
|
|
for name, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ok := isPermissionDenied(testCase.errMessage)
|
|
|
|
assert.Equal(t, testCase.ok, ok)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_extractInputPolicy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := map[string]struct {
|
|
line string
|
|
policy string
|
|
ok bool
|
|
}{
|
|
"empty line": {},
|
|
"random line": {
|
|
line: "random line",
|
|
},
|
|
"only first part": {
|
|
line: "Chain INPUT (policy ",
|
|
},
|
|
"empty policy": {
|
|
line: "Chain INPUT (policy )",
|
|
},
|
|
"ACCEPT policy": {
|
|
line: "Chain INPUT (policy ACCEPT)",
|
|
policy: "ACCEPT",
|
|
ok: true,
|
|
},
|
|
|
|
"ACCEPT policy with surrounding garbage": {
|
|
line: "garbage Chain INPUT (policy ACCEPT\t) )g()arbage",
|
|
policy: "ACCEPT",
|
|
ok: true,
|
|
},
|
|
}
|
|
|
|
for name, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
policy, ok := extractInputPolicy(testCase.line)
|
|
|
|
assert.Equal(t, testCase.policy, policy)
|
|
assert.Equal(t, testCase.ok, ok)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_randomInterfaceName(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const expectedRegex = `^[a-z0-9]{15}$`
|
|
interfaceName := randomInterfaceName()
|
|
assert.Regexp(t, expectedRegex, interfaceName)
|
|
}
|