{"record":{"id":"67e07db11aae1ac2","repo":"crowdsecurity/crowdsec","slug":"while-getting-process-attributes-both-plugin-user","errorCode":null,"errorMessage":"while getting process attributes: both plugin user and group must be set","messagePattern":"while getting process attributes: both plugin user and group must be set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/csplugin/utils.go","lineNumber":27,"sourceCode":"\t\"io/fs\"\n\t\"math\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc (pb *PluginBroker) CreateCmd(ctx context.Context, binaryPath string) (*exec.Cmd, error) {\n\tvar err error\n\tcmd := exec.CommandContext(ctx, binaryPath)\n\tif pb.pluginProcConfig.User != \"\" || pb.pluginProcConfig.Group != \"\" {\n\t\tif pb.pluginProcConfig.User == \"\" || pb.pluginProcConfig.Group == \"\" {\n\t\t\treturn nil, errors.New(\"while getting process attributes: both plugin user and group must be set\")\n\t\t}\n\t\tcmd.SysProcAttr, err = getProcessAttr(pb.pluginProcConfig.User, pb.pluginProcConfig.Group)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"while getting process attributes: %w\", err)\n\t\t}\n\t\tcmd.SysProcAttr.Credential.NoSetGroups = true\n\t}\n\treturn cmd, err\n}\n\nfunc getUID(username string) (uint32, error) {\n\tu, err := user.Lookup(username)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tuid, err := strconv.ParseInt(u.Uid, 10, 32)\n\tif err != nil {\n\t\treturn 0, err","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/csplugin/utils.go#L9-L45","documentation":"PluginBroker.CreateCmd can run notification plugins under a dedicated OS user/group via SysProcAttr credentials. The config allows specifying user and group independently, but dropping privileges on POSIX requires both; specifying only one is ambiguous, so CreateCmd rejects it before spawning.","triggerScenarios":"A plugin_common or plugin-specific config that sets 'user:' without 'group:' (or vice versa), when loadNotificationPlugin spawns the plugin binary.","commonSituations":"Hardening guides telling users to set a plugin user but omitting the group line; templated configs where one of the two fields is left blank.","solutions":["Set both keys in the plugin process config: user: crowdsec, group: crowdsec (or the desired uid/gid names)","If you do not need privilege dropping, remove both user and group so the plugin runs as the crowdsec process user","Verify the user and group exist on the system (getent passwd <user>; getent group <group>)"],"exampleFix":"// before (config.yaml)\nplugin_config:\n  user: crowdsec\n\n// after\nplugin_config:\n  user: crowdsec\n  group: crowdsec","handlingStrategy":"validation","validationCode":"if (pc.User == \"\") != (pc.Group == \"\") {\n    return fmt.Errorf(\"plugin user and group must both be set (got user=%q group=%q)\", pc.User, pc.Group)\n}","typeGuard":null,"tryCatchPattern":"if _, err := broker.CreateCmd(ctx, binPath); err != nil {\n    if strings.Contains(err.Error(), \"both plugin user and group must be set\") {\n        log.Fatal(\"set both user and group in the plugin process config, or neither\")\n    }\n    return err\n}","preventionTips":["Always configure user and group as a pair in plugin_common settings","Verify the chosen user/group exist on the host before enabling privilege dropping","Review hardening guides: set user AND group together"],"tags":["plugins","permissions","config"],"backgroundTag":"missing-required-config-field","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}