Adding uci firewall support

* Traffic Rules -> uci_firewall_rules.go
 * Redirections -> uci_firewall_redirect.go
 * Custom rules -> uci_firewall_custom_rules.go
This commit is contained in:
Philippe Caseiro 2018-11-07 16:36:45 +01:00
parent 3409ab1960
commit 77fe2a91dd
12 changed files with 230 additions and 38 deletions

23
uci.go
View File

@ -14,21 +14,24 @@ type Action struct {
// UCI "Object" // UCI "Object"
type UCI struct { type UCI struct {
exec Executor exec Executor
CustomFirewallFile string
Wireless *UCIWirelessConf Wireless *UCIWirelessConf
} }
// NewUCI return an UCI instance to interact with UCI // NewUCI return an UCI instance to interact with UCI
func NewUCI() *UCI { func NewUCI() *UCI {
exec := &localExecutor{} exec := &localExecutor{}
customFWFile := "/etc/"
wireless := &UCIWirelessConf{} wireless := &UCIWirelessConf{}
return &UCI{exec, wireless} return &UCI{exec, customFWFile, wireless}
} }
// NewUCIWithExecutor returns a UCI Instance an gives you the ability to provide // NewUCIWithExecutor returns a UCI Instance an gives you the ability to provide
// a different command executor than the default one. // a different command executor than the default one.
func NewUCIWithExecutor(exec Executor) *UCI { func NewUCIWithExecutor(exec Executor, customFWFile string) *UCI {
wireless := &UCIWirelessConf{} wireless := &UCIWirelessConf{}
return &UCI{exec, wireless} return &UCI{exec, customFWFile, wireless}
} }
// uciRun, private method to run the UCI command // uciRun, private method to run the UCI command
@ -157,3 +160,17 @@ func (u *UCI) GetWifiDeviceByName(name string) map[string]string {
} }
return nil return nil
} }
// Service make restart services via the UCI client possible
func (u *UCI) Service(name string, action string) error {
sys := NewSystemWithExecutor(u.exec)
res := sys.Service(name, action)
if res.ReturnCode != 0 {
return fmt.Errorf("%d - %s - %s - %s",
res.ReturnCode,
res.Command,
res.Stdout,
res.Stderr)
}
return nil
}

View File

@ -20,7 +20,7 @@ const (
func TestNetCreateWithDHCP(t *testing.T) { func TestNetCreateWithDHCP(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCINetworkInterface(uci) iface := NewUCINetworkInterface(uci)
iface.Name = ifNameDHCP iface.Name = ifNameDHCP
@ -44,7 +44,7 @@ func TestNetCreateWithDHCP(t *testing.T) {
func TestNetUpdateWithDHCP(t *testing.T) { func TestNetUpdateWithDHCP(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCINetworkInterface(uci) iface := NewUCINetworkInterface(uci)
iface.Name = ifNameDHCP iface.Name = ifNameDHCP
@ -80,7 +80,7 @@ func TestNetUpdateWithDHCP(t *testing.T) {
func TestNetDeleteWithDHCP(t *testing.T) { func TestNetDeleteWithDHCP(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCINetworkInterface(uci) iface := NewUCINetworkInterface(uci)
iface.Name = ifNameDHCP iface.Name = ifNameDHCP

View File

@ -0,0 +1,109 @@
package owrt
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
// UCIFirewallCustomRule is the description of an Wireless interface (cf Openwrt doc) on top of an Wireless Device
type UCIFirewallCustomRule struct {
Name string
Rule string
UCI *UCI
}
// NewUCIFirewallCustomRule builds a new UCIFirewallCustomRule instance
func NewUCIFirewallCustomRule(client *UCI) *UCIFirewallCustomRule {
return &UCIFirewallCustomRule{
UCI: client,
}
}
// Create add a new firewall rule in UCI Configuration
func (cr *UCIFirewallCustomRule) Create() error {
var file *os.File
customFWFile := cr.UCI.CustomFirewallFile
fmt.Printf("DEBUG %s\n", customFWFile)
_, stErr := os.Stat(customFWFile)
if os.IsNotExist(stErr) {
var err error
file, err = os.Create(customFWFile)
if err != nil {
return err
}
} else {
var oErr error
file, oErr = os.OpenFile(customFWFile, os.O_RDWR, 0644)
if oErr != nil {
return oErr
}
}
defer file.Close()
line := fmt.Sprintf("%s # %s", cr.Rule, cr.Name)
_, err := file.WriteString(line)
return err
}
// Save commit and relaod configuration (writes it to files !)
func (cr *UCIFirewallCustomRule) Save() error {
reload := cr.UCI.Reload()
if reload.ReturnCode != 0 {
return fmt.Errorf("%d - %s - %s - %s",
reload.ReturnCode,
reload.Command,
reload.Stdout,
reload.Stderr)
}
sErr := cr.UCI.Service("firewall", "restart")
return sErr
}
// Delete remove wifi interface from UCI Configuration
func (cr *UCIFirewallCustomRule) Delete() error {
input, err := ioutil.ReadFile(cr.UCI.CustomFirewallFile)
if err != nil {
return err
}
lines := strings.Split(string(input), "\n")
var out []string
for _, line := range lines {
if !strings.Contains(line, cr.Name) {
out = append(out, line)
}
}
output := strings.Join(out, "\n")
err = ioutil.WriteFile(cr.UCI.CustomFirewallFile, []byte(output), 0644)
return err
}
// Update add a new entry for wifi interface in UCI Configuration
func (cr *UCIFirewallCustomRule) Update() error {
input, err := ioutil.ReadFile(cr.UCI.CustomFirewallFile)
if err != nil {
return err
}
lines := strings.Split(string(input), "\n")
var out []string
for _, line := range lines {
if strings.Contains(line, cr.Name) {
nContent := fmt.Sprintf("%s # %s", cr.Rule, cr.Name)
out = append(out, nContent)
} else {
out = append(out, line)
}
}
output := strings.Join(out, "\n")
err = ioutil.WriteFile(cr.UCI.CustomFirewallFile, []byte(output), 0644)
return err
}

View File

@ -0,0 +1,71 @@
package owrt
import (
"io/ioutil"
"os"
"strings"
"testing"
)
const (
ruleA = "iptables -A PREROUTING -i br-dds -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.100.10.1:8443"
ruleB = "iptables -A PREROUTING -i br-dds -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.100.10.1:8080"
)
func TestFWCustomRuleCreate(t *testing.T) {
exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec, "/tmp/myCustomRuleFile")
_, sErr := os.Stat(uci.CustomFirewallFile)
if os.IsExist(sErr) {
rErr := os.Remove(uci.CustomFirewallFile)
if rErr != nil {
t.Fatal("Error cleaning temporary file")
}
}
custom := NewUCIFirewallCustomRule(uci)
custom.Name = "TestRule"
custom.Rule = ruleA
create := custom.Create()
if create != nil {
t.Fatalf("UCIFirewallCustomRule.Create() failed !\n%s", create.Error())
}
sv := custom.Save()
if sv != nil {
t.Fatalf("%s", sv.Error())
}
}
func TestFWCustomRuleUpdate(t *testing.T) {
exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec, "/tmp/myCustomRuleFile")
custom := NewUCIFirewallCustomRule(uci)
custom.Name = "SecondRule"
custom.Rule = ruleB
if cr := custom.Create(); cr != nil {
t.Fatalf("UCIFirewallCustomRule.Create() failed !\n%s", cr.Error())
}
b, err := ioutil.ReadFile(uci.CustomFirewallFile)
if err != nil {
t.Fatalf("%s", err.Error())
}
if !strings.Contains(string(b), ruleB) {
t.Fatalf("Rule is not present in %s file", uci.CustomFirewallFile)
}
custom.Rule = ruleA
if uErr := custom.Update(); uErr != nil {
t.Fatalf("UCIFirewallCustomRule.Update() faild ! %s", uErr.Error())
}
sv := custom.Save()
if sv != nil {
t.Fatalf("%s", sv.Error())
}
}

View File

@ -25,9 +25,7 @@ func NewUCIFirewallRedirect() *UCIFirewallRedirect {
// Create add a new firewall rule in UCI Configuration // Create add a new firewall rule in UCI Configuration
func (rd *UCIFirewallRedirect) Create(uci *UCI) *Action { func (rd *UCIFirewallRedirect) Create(uci *UCI) *Action {
var confPrefix string confPrefix := fmt.Sprintf("firewall.@redirect[%d]", rd.Index)
confPrefix = fmt.Sprintf("firewall.@redirect[%d]", rd.Index)
conf := make(map[string][]string) conf := make(map[string][]string)

View File

@ -18,7 +18,7 @@ const (
func TestFWRedirectCreate(t *testing.T) { func TestFWRedirectCreate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
redirect := NewUCIFirewallRedirect() redirect := NewUCIFirewallRedirect()
redirect.Name = redirectName redirect.Name = redirectName
@ -38,7 +38,7 @@ func TestFWRedirectCreate(t *testing.T) {
func TestFWRedirectUpdate(t *testing.T) { func TestFWRedirectUpdate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
redirect := NewUCIFirewallRedirect() redirect := NewUCIFirewallRedirect()
redirect.Name = redirectName redirect.Name = redirectName
@ -55,7 +55,7 @@ func TestFWRedirectUpdate(t *testing.T) {
t.Fatalf("UCIFirewallRedirect.Create() failed !") t.Fatalf("UCIFirewallRedirect.Create() failed !")
} }
redirect.Name = "Tutu" redirect.Name = "NewRedirect"
if redirect.Update(uci).ReturnCode != 0 { if redirect.Update(uci).ReturnCode != 0 {
t.Fatalf("UCIFirewallRedirect.Update() failed !") t.Fatalf("UCIFirewallRedirect.Update() failed !")
@ -64,7 +64,7 @@ func TestFWRedirectUpdate(t *testing.T) {
func TestFWRedirectDelete(t *testing.T) { func TestFWRedirectDelete(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
redirect := NewUCIFirewallRedirect() redirect := NewUCIFirewallRedirect()
redirect.Name = redirectName redirect.Name = redirectName

View File

@ -22,12 +22,9 @@ func NewUCIFirewallRule() *UCIFirewallRule {
// Create add a new firewall rule in UCI Configuration // Create add a new firewall rule in UCI Configuration
func (fw *UCIFirewallRule) Create(uci *UCI) *Action { func (fw *UCIFirewallRule) Create(uci *UCI) *Action {
var confPrefix string confPrefix := fmt.Sprintf("firewall.@rule[%d]", fw.Index)
confPrefix = fmt.Sprintf("firewall.@rule[%d]", fw.Index)
conf := make(map[string][]string) conf := make(map[string][]string)
conf["name"] = append(conf["network"], fmt.Sprintf("%s.name", confPrefix), fw.Name) conf["name"] = append(conf["network"], fmt.Sprintf("%s.name", confPrefix), fw.Name)
conf["src"] = append(conf["src"], fmt.Sprintf("%s.src", confPrefix), fw.Src) conf["src"] = append(conf["src"], fmt.Sprintf("%s.src", confPrefix), fw.Src)
conf["target"] = append(conf["target"], fmt.Sprintf("%s.target", confPrefix), fw.Target) conf["target"] = append(conf["target"], fmt.Sprintf("%s.target", confPrefix), fw.Target)

View File

@ -16,7 +16,7 @@ const (
func TestFWRuleCreate(t *testing.T) { func TestFWRuleCreate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
rule := NewUCIFirewallRule() rule := NewUCIFirewallRule()
rule.Name = ruleName rule.Name = ruleName
@ -34,7 +34,7 @@ func TestFWRuleCreate(t *testing.T) {
func TestFWRuleUpdate(t *testing.T) { func TestFWRuleUpdate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
rule := NewUCIFirewallRule() rule := NewUCIFirewallRule()
rule.Name = ruleName rule.Name = ruleName
@ -58,7 +58,7 @@ func TestFWRuleUpdate(t *testing.T) {
func TestFWRuleDelete(t *testing.T) { func TestFWRuleDelete(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
rule := NewUCIFirewallRule() rule := NewUCIFirewallRule()
rule.Name = ruleName rule.Name = ruleName

View File

@ -17,7 +17,7 @@ const (
func TestNetCreate(t *testing.T) { func TestNetCreate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCINetworkInterface(uci) iface := NewUCINetworkInterface(uci)
iface.Name = ifName iface.Name = ifName
@ -36,7 +36,7 @@ func TestNetCreate(t *testing.T) {
func TestNetUpdate(t *testing.T) { func TestNetUpdate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCINetworkInterface(uci) iface := NewUCINetworkInterface(uci)
iface.Name = ifName iface.Name = ifName
@ -61,7 +61,7 @@ func TestNetUpdate(t *testing.T) {
func TestNetDelete(t *testing.T) { func TestNetDelete(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCINetworkInterface(uci) iface := NewUCINetworkInterface(uci)
iface.Name = ifName iface.Name = ifName

View File

@ -8,7 +8,7 @@ import (
func TestUCIAdd(t *testing.T) { func TestUCIAdd(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
res := uci.Add("wireless", "test") res := uci.Add("wireless", "test")
if res.ReturnCode != 0 { if res.ReturnCode != 0 {
t.Error("Bad Return Code !") t.Error("Bad Return Code !")
@ -25,7 +25,7 @@ func TestUCIAdd(t *testing.T) {
func TestUCIAddFailed(t *testing.T) { func TestUCIAddFailed(t *testing.T) {
exec := createMockExecutor("", "BigError", 3) exec := createMockExecutor("", "BigError", 3)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
res := uci.Add("wireless", "test") res := uci.Add("wireless", "test")
if res.ReturnCode != 3 { if res.ReturnCode != 3 {
t.Error("Bad Return Code !") t.Error("Bad Return Code !")
@ -34,7 +34,7 @@ func TestUCIAddFailed(t *testing.T) {
func TestUCIDelete(t *testing.T) { func TestUCIDelete(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
res := uci.Delete("wireless.@wifi-iface[1]") res := uci.Delete("wireless.@wifi-iface[1]")
if res.ReturnCode != 0 { if res.ReturnCode != 0 {
t.Error("Bad Return Code !") t.Error("Bad Return Code !")
@ -51,7 +51,7 @@ func TestUCIDelete(t *testing.T) {
func TestUCISet(t *testing.T) { func TestUCISet(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
res := uci.Set("wireless.@wifi-iface[1].network", "OrionNetwork") res := uci.Set("wireless.@wifi-iface[1].network", "OrionNetwork")
if res.ReturnCode != 0 { if res.ReturnCode != 0 {
t.Error("Bad Return Code !") t.Error("Bad Return Code !")
@ -68,7 +68,7 @@ func TestUCISet(t *testing.T) {
func TestUCICommit(t *testing.T) { func TestUCICommit(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
res := uci.Commit() res := uci.Commit()
if res.ReturnCode != 0 { if res.ReturnCode != 0 {
t.Error("Bad Return Code !") t.Error("Bad Return Code !")
@ -85,7 +85,7 @@ func TestUCICommit(t *testing.T) {
func TestUCIReload(t *testing.T) { func TestUCIReload(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
res := uci.Reload() res := uci.Reload()
if res.ReturnCode != 0 { if res.ReturnCode != 0 {
t.Error("Bad Return Code !") t.Error("Bad Return Code !")
@ -106,7 +106,7 @@ func TestGetWifiIfaceBySSID(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
exec := createMockExecutor(string(config), "", 0) exec := createMockExecutor(string(config), "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
uci.LoadWirelessConf() uci.LoadWirelessConf()
wifi := uci.GetWifiIfaceBySSID("Pyxis2") wifi := uci.GetWifiIfaceBySSID("Pyxis2")
fmt.Printf("%s\n", wifi.Ssid) fmt.Printf("%s\n", wifi.Ssid)

View File

@ -11,7 +11,7 @@ func TestUCIGetWirelessConf(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
exec := createMockExecutor(string(config), "", 0) exec := createMockExecutor(string(config), "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
uci.LoadWirelessConf() uci.LoadWirelessConf()
if g, e := uci.Wireless.DefaultInterface["Name"], "wifi-iface"; g != e { if g, e := uci.Wireless.DefaultInterface["Name"], "wifi-iface"; g != e {
t.Fatalf("DefaultDevice.Name is expected to be [%s] and we have [%s]", e, g) t.Fatalf("DefaultDevice.Name is expected to be [%s] and we have [%s]", e, g)
@ -22,7 +22,7 @@ func TestUCIGetWirelessConf(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
exec = createMockExecutor(string(config), "", 0) exec = createMockExecutor(string(config), "", 0)
uci = NewUCIWithExecutor(exec) uci = NewUCIWithExecutor(exec, "")
uci.LoadWirelessConf() uci.LoadWirelessConf()
if g, e := uci.Wireless.Interfaces[1].Name, "wifi-iface"; g != e { if g, e := uci.Wireless.Interfaces[1].Name, "wifi-iface"; g != e {
t.Fatalf("DefaultDevice.Name is expected to be [%s] and we have [%s]", e, g) t.Fatalf("DefaultDevice.Name is expected to be [%s] and we have [%s]", e, g)

View File

@ -56,7 +56,7 @@ func TestGetSysDevName(t *testing.T) {
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCIWirelessInterface() iface := NewUCIWirelessInterface()
iface.Name = ifaceName iface.Name = ifaceName
@ -75,7 +75,7 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCIWirelessInterface() iface := NewUCIWirelessInterface()
iface.Name = ifaceName iface.Name = ifaceName
@ -102,7 +102,7 @@ func TestUpdate(t *testing.T) {
func TestDelete(t *testing.T) { func TestDelete(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCIWirelessInterface() iface := NewUCIWirelessInterface()
iface.Name = ifaceName iface.Name = ifaceName
@ -124,7 +124,7 @@ func TestDelete(t *testing.T) {
func TestConnect(t *testing.T) { func TestConnect(t *testing.T) {
exec := createMockExecutor("", "", 0) exec := createMockExecutor("", "", 0)
uci := NewUCIWithExecutor(exec) uci := NewUCIWithExecutor(exec, "")
iface := NewUCIWirelessInterface() iface := NewUCIWirelessInterface()
iface.Name = ifaceName iface.Name = ifaceName