Ver Fonte

allow notification plugins to work on freebsd and non-root functional tests (#1253)

* random uuid for all platforms
* check group writable and setgid; don't check group ownership
* allow user to run plugins without changing desired user/group (set them to "")
mmetc há 3 anos atrás
pai
commit
10ce45c054
2 ficheiros alterados com 53 adições e 25 exclusões
  1. 0 1
      go.sum
  2. 53 24
      pkg/csplugin/broker.go

+ 0 - 1
go.sum

@@ -311,7 +311,6 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
 github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
 github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
 github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
-github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=

+ 53 - 24
pkg/csplugin/broker.go

@@ -22,6 +22,7 @@ import (
 	"github.com/crowdsecurity/crowdsec/pkg/models"
 	"github.com/crowdsecurity/crowdsec/pkg/protobufs"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
+	"github.com/google/uuid"
 	plugin "github.com/hashicorp/go-plugin"
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
@@ -253,11 +254,13 @@ func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) (
 		return nil, err
 	}
 	cmd := exec.Command(binaryPath)
-	cmd.SysProcAttr, err = getProcessAtr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group)
-	if err != nil {
-		return nil, errors.Wrap(err, "while getting process attributes")
+	if pb.pluginProcConfig.User != "" || pb.pluginProcConfig.Group != "" {
+		cmd.SysProcAttr, err = getProcessAttr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group)
+		if err != nil {
+			return nil, errors.Wrap(err, "while getting process attributes")
+		}
+		cmd.SysProcAttr.Credential.NoSetGroups = true
 	}
-	cmd.SysProcAttr.Credential.NoSetGroups = true
 	pb.pluginMap[name] = &NotifierPlugin{}
 	l := log.New()
 	err = types.ConfigureLogger(l)
@@ -288,6 +291,7 @@ func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) (
 }
 
 func (pb *PluginBroker) pushNotificationsToPlugin(pluginName string, alerts []*models.Alert) error {
+	log.WithField("plugin", pluginName).Debug("pushing alerts to plugin")
 	if len(alerts) == 0 {
 		return nil
 	}
@@ -372,18 +376,27 @@ func pluginIsValid(path string) error {
 	if err != nil {
 		return errors.Wrap(err, "while getting current user")
 	}
-	procAttr, err := getProcessAtr(currentUser.Username, currentUser.Username)
+	currentUID, err := getUID(currentUser.Username)
 	if err != nil {
-		return errors.Wrap(err, "while getting process attributes")
+		return errors.Wrap(err, "while looking up the current uid")
 	}
 	stat := details.Sys().(*syscall.Stat_t)
-	if stat.Uid != procAttr.Credential.Uid || stat.Gid != procAttr.Credential.Gid {
-		return fmt.Errorf("plugin at %s is not owned by %s user and group", path, currentUser.Username)
+	if stat.Uid != currentUID {
+		return fmt.Errorf("plugin at %s is not owned by user '%s'", path, currentUser.Username)
 	}
 
-	if (int(details.Mode()) & 2) != 0 {
+	mode := details.Mode()
+	perm := uint32(mode)
+
+	if (perm & 00002) != 0 {
 		return fmt.Errorf("plugin at %s is world writable, world writable plugins are invalid", path)
 	}
+	if (perm & 00020) != 0 {
+		return fmt.Errorf("plugin at %s is group writable, group writable plugins are invalid", path)
+	}
+	if (mode & os.ModeSetgid) != 0 {
+		return fmt.Errorf("plugin at %s has setgid permission, which is not allowed", path)
+	}
 	return nil
 }
 
@@ -412,43 +425,59 @@ func getPluginTypeAndSubtypeFromPath(path string) (string, string, error) {
 	return strings.Join(parts[:len(parts)-1], "-"), parts[len(parts)-1], nil
 }
 
-func getProcessAtr(username string, groupname string) (*syscall.SysProcAttr, error) {
+func getUID(username string) (uint32, error) {
 	u, err := user.Lookup(username)
 	if err != nil {
-		return nil, err
-	}
-	g, err := user.LookupGroup(groupname)
-	if err != nil {
-		return nil, err
+		return 0, err
 	}
 	uid, err := strconv.ParseInt(u.Uid, 10, 32)
 	if err != nil {
-		return nil, err
+		return 0, err
 	}
 	if uid < 0 || uid > math.MaxInt32 {
-		return nil, fmt.Errorf("out of bound uid")
+		return 0, fmt.Errorf("out of bound uid")
+	}
+	return uint32(uid), nil
+}
+
+func getGID(groupname string) (uint32, error) {
+	g, err := user.LookupGroup(groupname)
+	if err != nil {
+		return 0, err
 	}
 	gid, err := strconv.ParseInt(g.Gid, 10, 32)
 	if err != nil {
-		return nil, err
+		return 0, err
 	}
 	if gid < 0 || gid > math.MaxInt32 {
-		return nil, fmt.Errorf("out of bound gid")
+		return 0, fmt.Errorf("out of bound gid")
+	}
+	return uint32(gid), nil
+}
+
+func getProcessAttr(username string, groupname string) (*syscall.SysProcAttr, error) {
+	uid, err := getUID(username)
+	if err != nil {
+		return nil, err
+	}
+	gid, err := getGID(groupname)
+	if err != nil {
+		return nil, err
 	}
 	return &syscall.SysProcAttr{
 		Credential: &syscall.Credential{
-			Uid: uint32(uid),
-			Gid: uint32(gid),
+			Uid: uid,
+			Gid: gid,
 		},
 	}, nil
 }
 
 func getUUID() (string, error) {
-	if d, err := os.ReadFile("/proc/sys/kernel/random/uuid"); err != nil {
+	uuidv4, err := uuid.NewRandom()
+	if err != nil {
 		return "", err
-	} else {
-		return string(d), nil
 	}
+	return uuidv4.String(), nil
 }
 
 func getHandshake() (plugin.HandshakeConfig, error) {