Add support for supplementary groups

Implement supplementary group support for services, allowing them to
access resources owned by multiple groups. Uses the @user:group,sup1,sup2
syntax to explicitly specify supplementary groups, in addition to now
reading group membership from /etc/group.
This commit is contained in:
Aaron Andersen
2025-12-24 09:54:37 -05:00
parent 702a606d26
commit b46592e818
3 changed files with 78 additions and 1 deletions
+16 -1
View File
@@ -116,10 +116,25 @@ Non-privileged Services
Every `run`, `task`, or `service` can also list the privileges the Every `run`, `task`, or `service` can also list the privileges the
`/path/to/cmd` should be executed with. Prefix the command with `/path/to/cmd` should be executed with. Prefix the command with
`@USR[:GRP]`, group is optional, like this: `@USR[:GRP[,SUPP,...]]`, where group and supplementary groups are
optional, like this:
run [2345] @joe:users logger "Hello world" run [2345] @joe:users logger "Hello world"
Finit reads the user's supplementary group membership from `/etc/group`
automatically. Any groups the user belongs to will be inherited by
the service.
To specify additional supplementary groups beyond those in `/etc/group`,
append them after the primary group, separated by commas:
service @caddy:caddy,ssl-cert /usr/bin/caddy run
This runs the `caddy` service as user `caddy`, with primary group
`caddy`, inheriting any groups `caddy` is a member of in `/etc/group`,
plus the additional `ssl-cert` group. This is useful when a service
needs access to resources owned by groups not listed in `/etc/group`.
For multiple instances of the same command, e.g. a DHCP client or For multiple instances of the same command, e.g. a DHCP client or
multiple web servers, add `:ID` somewhere between the `run`, `task`, multiple web servers, add `:ID` somewhere between the `run`, `task`,
`service` keyword and the command, like this: `service` keyword and the command, like this:
+59
View File
@@ -25,6 +25,7 @@
#include "config.h" /* Generated by configure script */ #include "config.h" /* Generated by configure script */
#include <ctype.h> /* isblank() */ #include <ctype.h> /* isblank() */
#include <grp.h> /* setgroups() */
#include <sched.h> /* sched_yield() */ #include <sched.h> /* sched_yield() */
#include <string.h> #include <string.h>
#include <sys/reboot.h> #include <sys/reboot.h>
@@ -560,6 +561,42 @@ static pid_t service_fork(svc_t *svc)
svc_ident(svc, NULL, 0), rlim2str(i)); svc_ident(svc, NULL, 0), rlim2str(i));
} }
#ifndef ENABLE_STATIC
/* Set supplementary groups from /etc/group and config */
{
gid_t supgids[NGROUPS_MAX];
int ngroups = NGROUPS_MAX;
int i, j, n = 0;
/* Get user's supplementary groups from /etc/group */
if (uid >= 0 && getgrouplist(svc->username, gid >= 0 ? gid : 0, supgids, &ngroups) >= 0)
n = ngroups;
/* Add explicitly configured supplementary groups */
for (i = 0; i < svc->num_supgroups && n < NGROUPS_MAX; i++) {
int g = getgroup(svc->supgroups[i]);
int found = 0;
if (g < 0) {
warn("%s: unknown supplementary group '%s'",
svc_ident(svc, NULL, 0), svc->supgroups[i]);
continue;
}
/* Skip if already in list from /etc/group */
for (j = 0; j < n; j++) {
if (supgids[j] == (gid_t)g) {
found = 1;
break;
}
}
if (!found)
supgids[n++] = g;
}
if (n > 0 && setgroups(n, supgids))
err(1, "%s: failed setgroups()", svc_ident(svc, NULL, 0));
}
#endif
/* Set desired user+group */ /* Set desired user+group */
if (gid >= 0) { if (gid >= 0) {
if (setgid(gid)) if (setgid(gid))
@@ -1957,7 +1994,29 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char *ptr = strchr(username, ':'); char *ptr = strchr(username, ':');
if (ptr) { if (ptr) {
char *sup;
*ptr++ = 0; *ptr++ = 0;
/* Check for supplementary groups: group,sup1,sup2,... */
sup = strchr(ptr, ',');
if (sup) {
*sup++ = 0;
svc->num_supgroups = 0;
while (sup) {
char *next = strchr(sup, ',');
if (next)
*next++ = 0;
if (svc->num_supgroups >= MAX_NUM_SUPGROUPS) {
warn("%s: too many supplementary groups, max %d",
svc->cmd, MAX_NUM_SUPGROUPS);
break;
}
strlcpy(svc->supgroups[svc->num_supgroups], sup,
sizeof(svc->supgroups[0]));
svc->num_supgroups++;
sup = next;
}
}
strlcpy(svc->group, ptr, sizeof(svc->group)); strlcpy(svc->group, ptr, sizeof(svc->group));
} }
strlcpy(svc->username, username, sizeof(svc->username)); strlcpy(svc->username, username, sizeof(svc->username));
+3
View File
@@ -101,6 +101,7 @@ typedef enum {
#define MAX_STR_LEN 64 #define MAX_STR_LEN 64
#define MAX_COND_LEN (MAX_ARG_LEN * 3) #define MAX_COND_LEN (MAX_ARG_LEN * 3)
#define MAX_USER_LEN 16 #define MAX_USER_LEN 16
#define MAX_NUM_SUPGROUPS 4
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */ #define MAX_NUM_FDS 64 /* Max number of I/O plugins */
#define MAX_NUM_SVC_ARGS 64 #define MAX_NUM_SVC_ARGS 64
@@ -191,6 +192,8 @@ typedef struct svc {
/* Identity */ /* Identity */
char username[MAX_USER_LEN]; char username[MAX_USER_LEN];
char group[MAX_USER_LEN]; char group[MAX_USER_LEN];
char supgroups[MAX_NUM_SUPGROUPS][MAX_USER_LEN];
int num_supgroups;
char capabilities[MAX_CMD_LEN]; char capabilities[MAX_CMD_LEN];
/* Command, arguments and service description */ /* Command, arguments and service description */