Skip to content

Commit

Permalink
controller: Allow configuring required selinux labels for node names
Browse files Browse the repository at this point in the history
This extends how we configure allowed node names, and allows us to
configure per-node-name requirements.

A node name can now either be in the allowed names list, or the
controller config (or via a .d dropin) can have a section named
"[agent FOO]", with a key Allowed=true.

If a named node is allowed, we look for the "RequiredSelinuxContext"
key in the above section, and if it is set, we require the connecting
agent to have the given selinux context type.

Note: We already disallow most processes (except bluechi_agent_t and
haproxy_t) to connect to the controller, but with this we can limit
what names they can claim. For example, we could enforce a special
name for an agent running in qm.
  • Loading branch information
alexlarsson committed Jan 10, 2025
1 parent aab58fb commit f9fbe48
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/controller/controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,34 @@ bool controller_apply_config(Controller *controller) {
}
}

_cleanup_freev_ char **sections = cfg_list_sections(controller->config);
if (sections == NULL) {
bc_log_error("Failed to list config sections");
return false;
}
for (size_t i = 0; sections[i] != NULL; i++) {
const char *section = sections[i];
if (!str_has_prefix(section, "agent ")) {
continue;
}
const char *agent = section + strlen("agent ");
Node *node = controller_find_node(controller, agent);
if (node == NULL) {
/* Add it to the list if it allowed */
bool allowed = cfg_s_get_bool_value(controller->config, section, CFG_ALLOWED);
if (allowed) {
node = controller_add_node(controller, agent);
} else {
continue;
}
}

const char *selinux_context = cfg_s_get_value(controller->config, section, CFG_REQUIRED_SELINUX_CONTEXT);
if (selinux_context && !node_set_required_selinux_context(node, selinux_context)) {
return false;
}
}

const char *interval_msec = cfg_get_value(controller->config, CFG_HEARTBEAT_INTERVAL);
if (interval_msec) {
if (!controller_set_heartbeat_interval(controller, interval_msec)) {
Expand Down
21 changes: 21 additions & 0 deletions src/controller/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ void node_unref(Node *node) {
free_and_null(node->name);
free_and_null(node->object_path);
free_and_null(node->peer_ip);
free_and_null(node->required_selinux_context);
free(node);
}

Expand All @@ -242,6 +243,14 @@ void node_shutdown(Node *node) {
}
}

bool node_set_required_selinux_context(Node *node, const char *selinux_context) {
node->required_selinux_context = strdup(selinux_context);
if (node->required_selinux_context == NULL) {
return false;
}
return true;
}

bool node_export(Node *node) {
Controller *controller = node->controller;

Expand Down Expand Up @@ -955,6 +964,18 @@ static int node_method_register(sd_bus_message *m, void *userdata, UNUSED sd_bus
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_SERVICE_UNKNOWN, "Unexpected node name");
}

if (named_node->required_selinux_context &&
(node->peer_selinux_context == NULL ||
!streq(node->peer_selinux_context, named_node->required_selinux_context))) {
bc_log_errorf("Node tried to register as '%s' with wrong selinux context '%s', expected '%s'",
name,
node->peer_selinux_context ? node->peer_selinux_context : "(missing)",
named_node->required_selinux_context);

/* This is same as for not allowed, don't allow probing for available names */
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_SERVICE_UNKNOWN, "Unexpected node name");
}

if (node_has_agent(named_node)) {
return sd_bus_reply_method_errorf(
m, SD_BUS_ERROR_ADDRESS_IN_USE, "The node is already connected");
Expand Down
2 changes: 2 additions & 0 deletions src/controller/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct Node {
LIST_FIELDS(Node, nodes);

char *name; /* NULL for not yet registered nodes */
char *required_selinux_context;
char *object_path;
char *peer_ip;
char *peer_selinux_context;
Expand Down Expand Up @@ -80,6 +81,7 @@ bool node_has_agent(Node *node);
bool node_is_online(Node *node);
bool node_set_agent_bus(Node *node, sd_bus *bus);
void node_unset_agent_bus(Node *node);
bool node_set_required_selinux_context(Node *node, const char *selinux_context);

AgentRequest *node_request_list_units(
Node *node, agent_request_response_t cb, void *userdata, free_func_t free_userdata);
Expand Down
2 changes: 2 additions & 0 deletions src/libbluechi/common/cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#define CFG_CONTROLLER_HOST "ControllerHost"
#define CFG_CONTROLLER_PORT "ControllerPort"
#define CFG_CONTROLLER_ADDRESS "ControllerAddress"
#define CFG_ALLOWED "ALLOWED"
#define CFG_REQUIRED_SELINUX_CONTEXT "RequiredSelinuxContext"
#define CFG_NODE_NAME "NodeName"
#define CFG_ALLOWED_NODE_NAMES "AllowedNodeNames"
#define CFG_HEARTBEAT_INTERVAL "HeartbeatInterval"
Expand Down

0 comments on commit f9fbe48

Please sign in to comment.