Skip to content

Commit

Permalink
WIP: libovsdb for acls & pgs
Browse files Browse the repository at this point in the history
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@redhat.com>
  • Loading branch information
jcaamano committed Jul 28, 2021
1 parent c2eed92 commit 1658ee9
Show file tree
Hide file tree
Showing 23 changed files with 1,564 additions and 1,652 deletions.
126 changes: 21 additions & 105 deletions go-controller/pkg/ovn/acl/acl.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,20 @@
package acl

import (
"encoding/json"
"fmt"
"strings"

libovsdbclient "github.com/ovn-org/libovsdb/client"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/libovsdbops"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"

"github.com/pkg/errors"

v1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
utilnet "k8s.io/utils/net"
)

// GetRejectACLs returns a map with the ACLs with a reject action
// the map uses the name of the ACL as key and the uuid as value
func GetRejectACLs() (map[string]string, error) {
// Get OVN's current reject ACLs. Note, currently only services use reject ACLs.
result := make(map[string]string)
type ovnACLData struct {
Data [][]interface{}
}
data, stderr, err := util.RunOVNNbctl("--columns=name,_uuid", "--format=json", "find", "acl", "action=reject")
if err != nil {
return result, errors.Wrapf(err, "Error while querying ACLs with reject action: %s", stderr)
}
// Process the output
x := ovnACLData{}
if err := json.Unmarshal([]byte(data), &x); err != nil {
return result, errors.Wrapf(err, "Unable to get current OVN reject ACLs. Unable to sync reject ACLs")
}
for _, entry := range x.Data {
// ACL entry format is a slice: [<aclName>, ["_uuid", <uuid>]]
if len(entry) != 2 {
continue
}
name, ok := entry[0].(string)
if !ok {
continue
}
uuidData, ok := entry[1].([]interface{})
if !ok || len(uuidData) != 2 {
continue
}
uuid, ok := uuidData[1].(string)
if !ok {
continue
}
result[name] = uuid
}
return result, nil
}

// RemoveACLFromNodeSwitches removes the ACL uuid entry from Logical Switch acl's list.
func RemoveACLFromNodeSwitches(switches []string, aclUUID string) error {
if len(switches) == 0 {
Expand All @@ -71,83 +32,38 @@ func RemoveACLFromNodeSwitches(switches []string, aclUUID string) error {
return nil
}

// RemoveACLFromPortGroup removes the ACL from the port-group
func RemoveACLFromPortGroup(aclUUID, clusterPortGroupUUID string) error {
_, stderr, err := util.RunOVNNbctl("--", "--if-exists", "remove", "port_group", clusterPortGroupUUID, "acls", aclUUID)
if err != nil {
return errors.Wrapf(err, "Failed to remove reject ACL %s from port group %s: stderr: %q", aclUUID, clusterPortGroupUUID, stderr)
}
klog.Infof("ACL: %s, removed from the port group : %s", aclUUID, clusterPortGroupUUID)
return nil
}

// AddRejectACLToPortGroup adds a reject ACL to a PortGroup
func AddRejectACLToPortGroup(clusterPortGroupUUID, aclName, sourceIP string, sourcePort int, proto v1.Protocol) (string, error) {
l3Prefix := "ip4"
if utilnet.IsIPv6String(sourceIP) {
l3Prefix = "ip6"
}

aclMatch := fmt.Sprintf("match=\"%s.dst==%s && %s && %s.dst==%d\"", l3Prefix, sourceIP,
strings.ToLower(string(proto)), strings.ToLower(string(proto)), sourcePort)
cmd := []string{"--id=@reject-acl", "create", "acl", "direction=" + types.DirectionFromLPort, "priority=" + types.DefaultDenyPriority, aclMatch, "action=reject",
fmt.Sprintf("name=%s", aclName), "--", "add", "port_group", clusterPortGroupUUID, "acls", "@reject-acl"}
aclUUID, stderr, err := util.RunOVNNbctl(cmd...)
if err != nil {
return "", errors.Wrapf(err, "Failed to add ACL: %s, %q, to cluster port group %s, stderr: %q", aclUUID, aclName, clusterPortGroupUUID, stderr)
}
return aclUUID, nil
}

// AddRejectACLToLogicalSwitch adds a reject ACL to a logical switch
func AddRejectACLToLogicalSwitch(logicalSwitch, aclName, sourceIP string, sourcePort int, proto v1.Protocol) (string, error) {
l3Prefix := "ip4"
if utilnet.IsIPv6String(sourceIP) {
l3Prefix = "ip6"
}

aclMatch := fmt.Sprintf("match=\"%s.dst==%s && %s && %s.dst==%d\"", l3Prefix, sourceIP,
strings.ToLower(string(proto)), strings.ToLower(string(proto)), sourcePort)
cmd := []string{"--id=@reject-acl", "create", "acl", "direction=" + types.DirectionFromLPort, "priority=" + types.DefaultDenyPriority, aclMatch, "action=reject",
fmt.Sprintf("name=%s", aclName), "--", "add", "logical_switch", logicalSwitch, "acls", "@reject-acl"}

aclUUID, stderr, err := util.RunOVNNbctl(cmd...)
func PurgeRejectRules(nbClient libovsdbclient.Client) error {
// TODO
// - Removing blindly reject ACLs might be problematic inf in the future we add this types of ACLs for other purposes
// - Do ACLs need to be removed or only references to them, which is what we seem to be doing elsewhere?
acls := []*nbdb.ACL{}
err := nbClient.WhereCache(func(acl *nbdb.ACL) bool {
return acl.Action == nbdb.ACLActionReject
}).List(acls)
if err != nil {
return "", errors.Wrapf(err, "Failed to add ACL: %s, %q, to cluster switch %s, stderr: %q", aclUUID, aclName, logicalSwitch, stderr)
return errors.Wrapf(err, "Error while querying ACLs with reject action: %v", err)
}
return aclUUID, nil
}

func PurgeRejectRules(clusterPortGroupUUID string) error {
data, stderr, err := util.RunOVNNbctl("--columns=_uuid", "--format=csv", "--data=bare", "--no-headings", "find", "acl", "action=reject")
if err != nil {
return errors.Wrapf(err, "Error while querying ACLs with reject action: %s", stderr)
}
if strings.TrimSpace(data) == "" {
if len(acls) == 0 {
klog.Info("No reject ACLs to remove")
return nil
}

for _, uuid := range strings.Split(data, "\n") {
err = RemoveACLFromPortGroup(uuid, clusterPortGroupUUID)
if err != nil {
klog.Errorf("Error trying to remove ACL for clusterPortGroupUUID %s/%s: %v", uuid, clusterPortGroupUUID, err)
}
err = libovsdbops.DeleteACLsFromPortGroup(nbClient, types.ClusterPortGroupName, acls...)
if err != nil {
klog.Errorf("Error trying to remove ACLs %+v frpm port group %s: %v", acls, types.ClusterPortGroupName, err)
}

data, stderr, err := util.RunOVNNbctl("--format=csv", "--data=bare", "--no-headings", "--columns=_uuid", "find", "logical_switch", fmt.Sprintf("acls{>=}%s", uuid))
for _, acl := range acls {
data, stderr, err := util.RunOVNNbctl("--format=csv", "--data=bare", "--no-headings", "--columns=_uuid", "find", "logical_switch", fmt.Sprintf("acls{>=}%s", acl.UUID))
if err != nil {
return errors.Wrapf(err, "Error while querying ACLs uuid:%s with reject action: %s", uuid, stderr)
return errors.Wrapf(err, "Error while querying ACLs uuid:%s with reject action: %s", acl.UUID, stderr)
}
ls := strings.Split(data, "\n")
err = RemoveACLFromNodeSwitches(ls, uuid)
err = RemoveACLFromNodeSwitches(ls, acl.UUID)
if err != nil {
return errors.Wrapf(err, "Failed to remove reject acl from logical switches")
}
_, stderr, err = util.RunOVNNbctl("--if-exists", "destroy", "acl", uuid)
if err != nil {
klog.Errorf("Failed to destroy ACL %s, stderr: %q, (%v)",
uuid, stderr, err)
}
}
return nil
}
205 changes: 0 additions & 205 deletions go-controller/pkg/ovn/acl/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ package acl

import (
"fmt"
"reflect"
"testing"

ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
v1 "k8s.io/api/core/v1"
)

func TestRemoveACLFromNodeSwitches(t *testing.T) {
Expand Down Expand Up @@ -65,205 +62,3 @@ func TestRemoveACLFromNodeSwitches(t *testing.T) {
})
}
}

func TestRemoveACLFromPortGroup(t *testing.T) {
tests := []struct {
name string
clusterPortGroupUUID string
aclUUID string
ovnCmd ovntest.ExpectedCmd
wantErr bool
}{
{
name: "remove acl from portgroup",
clusterPortGroupUUID: "63c3e636-387d-11eb-ac78-a8a1590cda29",
aclUUID: "a08ea426-2288-11eb-a30b-a8a1590cda29",
ovnCmd: ovntest.ExpectedCmd{
Cmd: "ovn-nbctl --timeout=15 -- --if-exists remove port_group 63c3e636-387d-11eb-ac78-a8a1590cda29 acls a08ea426-2288-11eb-a30b-a8a1590cda29",
Output: "",
},
wantErr: false,
},

// TODO: Add test cases for errors
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fexec := ovntest.NewLooseCompareFakeExec()
fexec.AddFakeCmd(&tt.ovnCmd)
err := util.SetExec(fexec)
if err != nil {
t.Errorf("fexec error: %v", err)
}

err = RemoveACLFromPortGroup(tt.aclUUID, tt.clusterPortGroupUUID)
if (err != nil) != tt.wantErr {
t.Errorf("RemoveACLFromPortGroup() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}

func TestGetRejectACLs(t *testing.T) {
tests := []struct {
name string
ovnCmd ovntest.ExpectedCmd
want map[string]string
wantErr bool
}{
{
name: "no reject ACLs in OVN",
ovnCmd: ovntest.ExpectedCmd{
Cmd: "ovn-nbctl --timeout=15 --columns=name,_uuid --format=json find acl action=reject",
Output: `{"data":[],"headings":["name","_uuid"]}`,
},
want: make(map[string]string),
wantErr: false,
},

// TODO: Add test cases with some data
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fexec := ovntest.NewLooseCompareFakeExec()
fexec.AddFakeCmd(&tt.ovnCmd)
err := util.SetExec(fexec)
if err != nil {
t.Errorf("fexec error: %v", err)
}
got, err := GetRejectACLs()
if (err != nil) != tt.wantErr {
t.Errorf("GetRejectACLs() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetRejectACLs() = %v, want %v", got, tt.want)
}
})
}
}

func TestAddRejectACLToLogicalSwitch(t *testing.T) {
tests := []struct {
name string
logicalSwitch string
aclName string
sourceIP string
sourcePort int
proto v1.Protocol
ovnCmd ovntest.ExpectedCmd
want string
wantErr bool
}{
{
name: "add ipv4 acl",
logicalSwitch: "545dc436-387e-11eb-9f38-a8a1590cda29",
aclName: "myacl",
sourceIP: "192.168.2.2",
sourcePort: 80,
proto: v1.ProtocolTCP,
ovnCmd: ovntest.ExpectedCmd{
Cmd: `ovn-nbctl --timeout=15 --id=@reject-acl create acl direction=` + types.DirectionFromLPort + ` priority=` + types.DefaultDenyPriority + ` match="ip4.dst==192.168.2.2 && tcp && tcp.dst==80" action=reject name=myacl -- add logical_switch 545dc436-387e-11eb-9f38-a8a1590cda29 acls @reject-acl`,
Output: "97347886-387e-11eb-9fdf-a8a1590cda29",
},
want: "97347886-387e-11eb-9fdf-a8a1590cda29",
wantErr: false,
},
{
name: "add ipv6 acl",
logicalSwitch: "545dc436-387e-11eb-9f38-a8a1590cda29",
aclName: "myacl",
sourceIP: "2001:db2:1:2::23",
sourcePort: 80,
proto: v1.ProtocolTCP,
ovnCmd: ovntest.ExpectedCmd{
Cmd: `ovn-nbctl --timeout=15 --id=@reject-acl create acl direction=` + types.DirectionFromLPort + ` priority=` + types.DefaultDenyPriority + ` match="ip6.dst==2001:db2:1:2::23 && tcp && tcp.dst==80" action=reject name=myacl -- add logical_switch 545dc436-387e-11eb-9f38-a8a1590cda29 acls @reject-acl`,
Output: "97347886-387e-11eb-9fdf-a8a1590cda29",
},
want: "97347886-387e-11eb-9fdf-a8a1590cda29",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fexec := ovntest.NewLooseCompareFakeExec()
fexec.AddFakeCmd(&tt.ovnCmd)
err := util.SetExec(fexec)
if err != nil {
t.Errorf("fexec error: %v", err)
}

got, err := AddRejectACLToLogicalSwitch(tt.logicalSwitch, tt.aclName, tt.sourceIP, tt.sourcePort, tt.proto)
if (err != nil) != tt.wantErr {
t.Errorf("AddRejectACLToLogicalSwitch() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("AddRejectACLToLogicalSwitch() = %v, want %v", got, tt.want)
}
})
}
}

func TestAddRejectACLToPortGroup(t *testing.T) {
tests := []struct {
name string
portGroup string
aclName string
sourceIP string
sourcePort int
proto v1.Protocol
ovnCmd ovntest.ExpectedCmd
want string
wantErr bool
}{
{
name: "add ipv4 acl",
portGroup: "545dc436-387e-11eb-9f38-a8a1590cda29",
aclName: "myacl",
sourceIP: "192.168.2.2",
sourcePort: 80,
proto: v1.ProtocolTCP,
ovnCmd: ovntest.ExpectedCmd{
Cmd: `ovn-nbctl --timeout=15 --id=@reject-acl create acl direction=` + types.DirectionFromLPort + ` priority=` + types.DefaultDenyPriority + ` match="ip4.dst==192.168.2.2 && tcp && tcp.dst==80" action=reject name=myacl -- add port_group 545dc436-387e-11eb-9f38-a8a1590cda29 acls @reject-acl`,
Output: "97347886-387e-11eb-9fdf-a8a1590cda29",
},
want: "97347886-387e-11eb-9fdf-a8a1590cda29",
wantErr: false,
},
{
name: "add ipv6 acl",
portGroup: "545dc436-387e-11eb-9f38-a8a1590cda29",
aclName: "myacl",
sourceIP: "2001:db2:1:2::23",
sourcePort: 80,
proto: v1.ProtocolTCP,
ovnCmd: ovntest.ExpectedCmd{
Cmd: `ovn-nbctl --timeout=15 --id=@reject-acl create acl direction=` + types.DirectionFromLPort + ` priority=` + types.DefaultDenyPriority + ` match="ip6.dst==2001:db2:1:2::23 && tcp && tcp.dst==80" action=reject name=myacl -- add port_group 545dc436-387e-11eb-9f38-a8a1590cda29 acls @reject-acl`,
Output: "97347886-387e-11eb-9fdf-a8a1590cda29",
},
want: "97347886-387e-11eb-9fdf-a8a1590cda29",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fexec := ovntest.NewLooseCompareFakeExec()
fexec.AddFakeCmd(&tt.ovnCmd)
err := util.SetExec(fexec)
if err != nil {
t.Errorf("fexec error: %v", err)
}

got, err := AddRejectACLToPortGroup(tt.portGroup, tt.aclName, tt.sourceIP, tt.sourcePort, tt.proto)
if (err != nil) != tt.wantErr {
t.Errorf("AddRejectACLToPortGroup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("AddRejectACLToPortGroup() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit 1658ee9

Please sign in to comment.