mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
core: add user and group to NFTSet=
The benefit of using this setting is that user and group IDs, especially dynamic and random IDs used by DynamicUser=, can be used in firewall configuration easily. Example: ``` [Service] NFTSet=user:inet:filter:serviceuser ``` Corresponding NFT rules: ``` table inet filter { set serviceuser { typeof meta skuid } chain service_output { meta skuid @serviceuser accept drop } } ``` ``` $ cat /etc/systemd/system/dunft.service [Service] DynamicUser=yes NFTSet=user:inet:filter:serviceuser ExecStart=/bin/sleep 1000 [Install] WantedBy=multi-user.target $ sudo nft list set inet filter serviceuser table inet filter { set serviceuser { typeof meta skuid elements = { 64864 } } } $ ps -n --format user,group,pid,command -p `systemctl show dunft.service -P MainPID` USER GROUP PID COMMAND 64864 64864 55158 /bin/sleep 1000 ```
This commit is contained in:
parent
dc7d69b3c1
commit
3bb48b19bd
@ -1504,24 +1504,26 @@ DeviceAllow=/dev/loop-control
|
||||
<varlistentry>
|
||||
<term><varname>NFTSet=</varname><replaceable>family</replaceable>:<replaceable>table</replaceable>:<replaceable>set</replaceable></term>
|
||||
<listitem>
|
||||
<para>This setting provides a method for integrating dynamic cgroup IDs into firewall rules with
|
||||
<ulink url="https://netfilter.org/projects/nftables/index.html">NFT</ulink> sets. The benefit of
|
||||
using this setting is to be able to use the IDs as selectors in firewall rules easily and this in
|
||||
turn allows more fine grained filtering. NFT rules for cgroup matching use numeric cgroup IDs,
|
||||
which change every time a service is restarted, making them hard to use in systemd environment
|
||||
otherwise.</para>
|
||||
<para>This setting provides a method for integrating dynamic cgroup, user and group IDs into
|
||||
firewall rules with <ulink url="https://netfilter.org/projects/nftables/index.html">NFT</ulink>
|
||||
sets. The benefit of using this setting is to be able to use the IDs as selectors in firewall rules
|
||||
easily and this in turn allows more fine grained filtering. NFT rules for cgroup matching use
|
||||
numeric cgroup IDs, which change every time a service is restarted, making them hard to use in
|
||||
systemd environment otherwise. Dynamic and random IDs used by <varname>DynamicUser=</varname> can
|
||||
be also integrated with this setting.</para>
|
||||
|
||||
<para>This option expects a whitespace separated list of NFT set definitions. Each definition
|
||||
consists of a colon-separated tuple of source type (only <literal>cgroup</literal>), NFT address
|
||||
family (one of <literal>arp</literal>, <literal>bridge</literal>, <literal>inet</literal>,
|
||||
<literal>ip</literal>, <literal>ip6</literal>, or <literal>netdev</literal>), table name and set
|
||||
name. The names of tables and sets must conform to lexical restrictions of NFT table names. The
|
||||
type of the element used in the NFT filter must match the type implied by the directive
|
||||
(<literal>cgroup</literal>) as shown in the table below. When a control group is realized, the
|
||||
corresponding ID will be appended to the NFT sets and it will be be removed when the control group
|
||||
is removed. <command>systemd</command> only inserts elements to (or removes from) the sets, so the
|
||||
related NFT rules, tables and sets must be prepared elsewhere in advance. Failures to manage the
|
||||
sets will be ignored.</para>
|
||||
consists of a colon-separated tuple of source type (one of <literal>cgroup</literal>,
|
||||
<literal>user</literal> or <literal>group</literal>), NFT address family (one of
|
||||
<literal>arp</literal>, <literal>bridge</literal>, <literal>inet</literal>, <literal>ip</literal>,
|
||||
<literal>ip6</literal>, or <literal>netdev</literal>), table name and set name. The names of tables
|
||||
and sets must conform to lexical restrictions of NFT table names. The type of the element used in
|
||||
the NFT filter must match the type implied by the directive (<literal>cgroup</literal>,
|
||||
<literal>user</literal> or <literal>group</literal>) as shown in the table below. When a control
|
||||
group or a unit is realized, the corresponding ID will be appended to the NFT sets and it will be
|
||||
be removed when the control group or unit is removed. <command>systemd</command> only inserts
|
||||
elements to (or removes from) the sets, so the related NFT rules, tables and sets must be prepared
|
||||
elsewhere in advance. Failures to manage the sets will be ignored.</para>
|
||||
|
||||
<table>
|
||||
<title>Defined <varname>source type</varname> values</title>
|
||||
@ -1543,6 +1545,16 @@ DeviceAllow=/dev/loop-control
|
||||
<entry>control group ID</entry>
|
||||
<entry><literal>cgroupsv2</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>user</literal></entry>
|
||||
<entry>user ID</entry>
|
||||
<entry><literal>meta skuid</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>group</literal></entry>
|
||||
<entry>group ID</entry>
|
||||
<entry><literal>meta skgid</literal></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
@ -1552,17 +1564,24 @@ DeviceAllow=/dev/loop-control
|
||||
|
||||
<para>Example:
|
||||
<programlisting>[Unit]
|
||||
NFTSet=cgroup:inet:filter:my_service
|
||||
NFTSet=cgroup:inet:filter:my_service user:inet:filter:serviceuser
|
||||
</programlisting>
|
||||
Corresponding NFT rules:
|
||||
<programlisting>table inet filter {
|
||||
set my_service {
|
||||
type cgroupsv2
|
||||
}
|
||||
set serviceuser {
|
||||
typeof meta skuid
|
||||
}
|
||||
chain x {
|
||||
socket cgroupv2 level 2 @my_service accept
|
||||
drop
|
||||
}
|
||||
chain y {
|
||||
meta skuid @serviceuser accept
|
||||
drop
|
||||
}
|
||||
}</programlisting>
|
||||
</para>
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
|
@ -2235,7 +2235,7 @@ int bus_cgroup_set_property(
|
||||
while ((r = sd_bus_message_read(message, "(iiss)", &source, &nfproto, &table, &set)) > 0) {
|
||||
const char *source_name, *nfproto_name;
|
||||
|
||||
if (source != NFT_SET_SOURCE_CGROUP)
|
||||
if (!IN_SET(source, NFT_SET_SOURCE_CGROUP, NFT_SET_SOURCE_USER, NFT_SET_SOURCE_GROUP))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid source %d.", source);
|
||||
|
||||
source_name = nft_set_source_to_string(source);
|
||||
|
@ -5136,6 +5136,41 @@ PidRef* unit_main_pid(Unit *u) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void unit_modify_user_nft_set(Unit *u, bool add, NFTSetSource source, uint32_t element) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!MANAGER_IS_SYSTEM(u->manager))
|
||||
return;
|
||||
|
||||
CGroupContext *c;
|
||||
c = unit_get_cgroup_context(u);
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (!u->manager->fw_ctx) {
|
||||
r = fw_ctx_new_full(&u->manager->fw_ctx, /* init_tables= */ false);
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
assert(u->manager->fw_ctx);
|
||||
}
|
||||
|
||||
FOREACH_ARRAY(nft_set, c->nft_set_context.sets, c->nft_set_context.n_sets) {
|
||||
if (nft_set->source != source)
|
||||
continue;
|
||||
|
||||
r = nft_set_element_modify_any(u->manager->fw_ctx, add, nft_set->nfproto, nft_set->table, nft_set->set, &element, sizeof(element));
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, ID %u, ignoring: %m",
|
||||
add? "add" : "delete", nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set, element);
|
||||
else
|
||||
log_debug("%s NFT set: family %s, table %s, set %s, ID %u",
|
||||
add? "Added" : "Deleted", nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set, element);
|
||||
}
|
||||
}
|
||||
|
||||
static void unit_unref_uid_internal(
|
||||
Unit *u,
|
||||
uid_t *ref_uid,
|
||||
@ -5162,10 +5197,18 @@ static void unit_unref_uid_internal(
|
||||
}
|
||||
|
||||
static void unit_unref_uid(Unit *u, bool destroy_now) {
|
||||
assert(u);
|
||||
|
||||
unit_modify_user_nft_set(u, /* add = */ false, NFT_SET_SOURCE_USER, u->ref_uid);
|
||||
|
||||
unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
|
||||
}
|
||||
|
||||
static void unit_unref_gid(Unit *u, bool destroy_now) {
|
||||
assert(u);
|
||||
|
||||
unit_modify_user_nft_set(u, /* add = */ false, NFT_SET_SOURCE_GROUP, u->ref_gid);
|
||||
|
||||
unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
|
||||
}
|
||||
|
||||
@ -5260,6 +5303,9 @@ int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) {
|
||||
if (r < 0)
|
||||
return log_unit_warning_errno(u, r, "Couldn't add UID/GID reference to unit, proceeding without: %m");
|
||||
|
||||
unit_modify_user_nft_set(u, /* add = */ true, NFT_SET_SOURCE_USER, uid);
|
||||
unit_modify_user_nft_set(u, /* add = */ true, NFT_SET_SOURCE_GROUP, gid);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -509,7 +509,7 @@ static int bus_append_nft_set(sd_bus_message *m, const char *field, const char *
|
||||
assert(set);
|
||||
|
||||
source = nft_set_source_from_string(source_str);
|
||||
if (source != NFT_SET_SOURCE_CGROUP)
|
||||
if (!IN_SET(source, NFT_SET_SOURCE_CGROUP, NFT_SET_SOURCE_USER, NFT_SET_SOURCE_GROUP))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s", field);
|
||||
|
||||
nfproto = nfproto_from_string(nfproto_str);
|
||||
|
@ -1203,6 +1203,8 @@ static const char *const nft_set_source_table[] = {
|
||||
[NFT_SET_SOURCE_PREFIX] = "prefix",
|
||||
[NFT_SET_SOURCE_IFINDEX] = "ifindex",
|
||||
[NFT_SET_SOURCE_CGROUP] = "cgroup",
|
||||
[NFT_SET_SOURCE_USER] = "user",
|
||||
[NFT_SET_SOURCE_GROUP] = "group",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(nft_set_source, int);
|
||||
@ -1223,7 +1225,7 @@ int nft_set_add(NFTSetContext *s, NFTSetSource source, int nfproto, const char *
|
||||
_cleanup_free_ char *table_dup = NULL, *set_dup = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(IN_SET(source, NFT_SET_SOURCE_ADDRESS, NFT_SET_SOURCE_PREFIX, NFT_SET_SOURCE_IFINDEX, NFT_SET_SOURCE_CGROUP));
|
||||
assert(IN_SET(source, NFT_SET_SOURCE_ADDRESS, NFT_SET_SOURCE_PREFIX, NFT_SET_SOURCE_IFINDEX, NFT_SET_SOURCE_CGROUP, NFT_SET_SOURCE_USER, NFT_SET_SOURCE_GROUP));
|
||||
assert(nfproto_is_valid(nfproto));
|
||||
assert(table);
|
||||
assert(set);
|
||||
@ -1332,7 +1334,7 @@ int config_parse_nft_set(
|
||||
source = nft_set_source_from_string(source_str);
|
||||
if (source < 0 ||
|
||||
(ltype == NFT_SET_PARSE_NETWORK && !IN_SET(source, NFT_SET_SOURCE_ADDRESS, NFT_SET_SOURCE_PREFIX, NFT_SET_SOURCE_IFINDEX)) ||
|
||||
(ltype == NFT_SET_PARSE_CGROUP && source != NFT_SET_SOURCE_CGROUP)) {
|
||||
(ltype == NFT_SET_PARSE_CGROUP && !IN_SET(source, NFT_SET_SOURCE_CGROUP, NFT_SET_SOURCE_USER, NFT_SET_SOURCE_GROUP))) {
|
||||
_cleanup_free_ char *esc = NULL;
|
||||
|
||||
esc = cescape(source_str);
|
||||
|
@ -37,6 +37,8 @@ typedef enum NFTSetSource {
|
||||
NFT_SET_SOURCE_PREFIX,
|
||||
NFT_SET_SOURCE_IFINDEX,
|
||||
NFT_SET_SOURCE_CGROUP,
|
||||
NFT_SET_SOURCE_USER,
|
||||
NFT_SET_SOURCE_GROUP,
|
||||
_NFT_SET_SOURCE_MAX,
|
||||
_NFT_SET_SOURCE_INVALID = -EINVAL,
|
||||
} NFTSetSource;
|
||||
|
Loading…
Reference in New Issue
Block a user