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
```
<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>
<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>
<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>
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);
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,
}
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);
}
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;
}
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);
[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);
_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);
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);
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;