networkd: NetLabel integration
New directive `NetLabel=` provides a method for integrating dynamic network
configuration into Linux NetLabel subsystem rules, used by Linux security
modules (LSMs) for network access control. The option expects a whitespace
separated list of NetLabel labels. The labels must conform to lexical
restrictions of LSM labels. When an interface is configured with IP addresses,
the addresses and subnetwork masks will be appended to the NetLabel Fallback
Peer Labeling rules. They will be removed when the interface is
deconfigured. Failures to manage the labels will be ignored.
Example:
```
[DHCP]
NetLabel=system_u:object_r:localnet_peer_t:s0
```
With the above rules for interface `eth0`, when the interface is configured with
an IPv4 address of 10.0.0.0/8, `systemd-networkd` performs the equivalent of
`netlabelctl` operation
```
$ sudo netlabelctl unlbl add interface eth0 address:10.0.0.0/8 label:system_u:object_r:localnet_peer_t:s0
```
Result:
```
$ sudo netlabelctl -p unlbl list
...
interface: eth0
address: 10.0.0.0/8
label: "system_u:object_r:localnet_peer_t:s0"
...
```
This commit is contained in:
committed by
Topi Miettinen
parent
cc9e3a55d3
commit
3cf63830ac
@@ -115,6 +115,8 @@ sources = files(
|
||||
'networkd-ndisc.h',
|
||||
'networkd-neighbor.c',
|
||||
'networkd-neighbor.h',
|
||||
'networkd-netlabel.c',
|
||||
'networkd-netlabel.h',
|
||||
'networkd-network-bus.c',
|
||||
'networkd-network-bus.h',
|
||||
'networkd-network.c',
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "networkd-dhcp-server.h"
|
||||
#include "networkd-ipv4acd.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-netlabel.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-route-util.h"
|
||||
@@ -137,6 +138,7 @@ Address *address_free(Address *address) {
|
||||
|
||||
config_section_free(address->section);
|
||||
free(address->label);
|
||||
set_free(address->netlabels);
|
||||
return mfree(address);
|
||||
}
|
||||
|
||||
@@ -492,6 +494,8 @@ static int address_update(Address *address) {
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
|
||||
|
||||
address_add_netlabel(address);
|
||||
|
||||
if (address_is_ready(address) && address->callback) {
|
||||
r = address->callback(address);
|
||||
if (r < 0)
|
||||
@@ -518,6 +522,8 @@ static int address_drop(Address *address) {
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
|
||||
|
||||
address_del_netlabel(address);
|
||||
|
||||
if (address->state == 0)
|
||||
address_free(address);
|
||||
|
||||
@@ -1939,6 +1945,41 @@ int config_parse_duplicate_address_detection(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_address_netlabel(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Network *network = userdata;
|
||||
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
assert(network);
|
||||
|
||||
r = address_new_static(network, filename, section_line, &n);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to allocate new address, ignoring assignment: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return config_parse_netlabel(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->netlabels, network);
|
||||
}
|
||||
|
||||
static int address_section_verify(Address *address) {
|
||||
if (section_is_invalid(address->section))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -61,6 +61,9 @@ struct Address {
|
||||
|
||||
/* Called when address become ready */
|
||||
address_ready_callback_t callback;
|
||||
|
||||
/* NetLabel */
|
||||
Set *netlabels;
|
||||
};
|
||||
|
||||
const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) _warn_unused_result_;
|
||||
@@ -135,3 +138,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_address_flags);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_address_scope);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_address_route_metric);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_address_netlabel);
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "netlink-util.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-netlabel.h"
|
||||
#include "networkd-network.h"
|
||||
|
||||
static int netlabel_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
int r;
|
||||
|
||||
assert_se(rtnl);
|
||||
assert_se(m);
|
||||
assert_se(link);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0) {
|
||||
log_link_message_warning_errno(link, m, r, "NetLabel operation failed, ignoring");
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_link_debug(link, "NetLabel operation successful");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int netlabel_command(uint16_t command, const char *label, const Address *address) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
assert(address->link->manager);
|
||||
assert(address->link->manager->genl);
|
||||
assert(address->link->network);
|
||||
assert(IN_SET(address->family, AF_INET, AF_INET6));
|
||||
|
||||
r = sd_genl_message_new(address->link->manager->genl, NETLBL_NLTYPE_UNLABELED_NAME, command, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NLBL_UNLABEL_A_IFACE, address->link->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (command == NLBL_UNLABEL_C_STATICADD) {
|
||||
assert(label);
|
||||
r = sd_netlink_message_append_string(m, NLBL_UNLABEL_A_SECCTX, label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
union in_addr_union netmask;
|
||||
|
||||
r = in_addr_prefixlen_to_netmask(address->family, &netmask, address->prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (address->family == AF_INET) {
|
||||
r = sd_netlink_message_append_in_addr(m, NLBL_UNLABEL_A_IPV4ADDR, &address->in_addr.in);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_in_addr(m, NLBL_UNLABEL_A_IPV4MASK, &netmask.in);
|
||||
} else if (address->family == AF_INET6) {
|
||||
r = sd_netlink_message_append_in6_addr(m, NLBL_UNLABEL_A_IPV6ADDR, &address->in_addr.in6);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_in6_addr(m, NLBL_UNLABEL_A_IPV6MASK, &netmask.in6);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netlink_call_async(address->link->manager->genl, NULL, m, netlabel_handler, link_netlink_destroy_callback,
|
||||
address->link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
link_ref(address->link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void address_add_netlabel_set(const Address *address, Set *labels) {
|
||||
_cleanup_free_ char *addr_str = NULL;
|
||||
int r;
|
||||
const char *label;
|
||||
|
||||
(void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &addr_str);
|
||||
|
||||
SET_FOREACH(label, labels) {
|
||||
r = netlabel_command(NLBL_UNLABEL_C_STATICADD, label, address);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(address->link, r, "Adding NetLabel %s for IP address %s failed, ignoring",
|
||||
label, strna(addr_str));
|
||||
else
|
||||
log_link_debug(address->link, "Adding NetLabel %s for IP address %s", label, strna(addr_str));
|
||||
}
|
||||
}
|
||||
|
||||
void address_add_netlabel(const Address *address) {
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
|
||||
if (!address->link->network || !IN_SET(address->family, AF_INET, AF_INET6))
|
||||
return;
|
||||
|
||||
switch (address->source) {
|
||||
case NETWORK_CONFIG_SOURCE_DHCP4:
|
||||
return address_add_netlabel_set(address, address->link->network->dhcp_netlabels);
|
||||
case NETWORK_CONFIG_SOURCE_DHCP6:
|
||||
return address_add_netlabel_set(address, address->link->network->dhcp6_netlabels);
|
||||
case NETWORK_CONFIG_SOURCE_DHCP_PD:
|
||||
return address_add_netlabel_set(address, address->link->network->dhcp_pd_netlabels);
|
||||
case NETWORK_CONFIG_SOURCE_NDISC:
|
||||
return address_add_netlabel_set(address, address->link->network->ndisc_netlabels);
|
||||
case NETWORK_CONFIG_SOURCE_STATIC:
|
||||
return address_add_netlabel_set(address, address->netlabels);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void address_del_netlabel(const Address *address) {
|
||||
int r;
|
||||
_cleanup_free_ char *addr_str = NULL;
|
||||
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
|
||||
if (!address->link->network || !IN_SET(address->family, AF_INET, AF_INET6))
|
||||
return;
|
||||
|
||||
(void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &addr_str);
|
||||
|
||||
r = netlabel_command(NLBL_UNLABEL_C_STATICREMOVE, NULL, address);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(address->link, r, "Deleting NetLabels for IP address %s failed, ignoring",
|
||||
strna(addr_str));
|
||||
else
|
||||
log_link_debug(address->link, "Deleting NetLabels for IP address %s", strna(addr_str));
|
||||
}
|
||||
|
||||
int config_parse_netlabel(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
int r;
|
||||
Set **set = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(set);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*set = set_free(*set);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const char *p = rvalue;;) {
|
||||
_cleanup_free_ char *w = NULL;
|
||||
|
||||
r = extract_first_word(&p, &w, NULL, 0);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to extract NetLabel label, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
/* Label semantics depend on LSM but let's do basic checks */
|
||||
if (!string_is_safe(w)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Bad NetLabel label, ignoring: %s", w);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = set_ensure_consume(set, &string_hash_ops_free, TAKE_PTR(w));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
void address_add_netlabel(const Address *address);
|
||||
void address_del_netlabel(const Address *address);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_netlabel);
|
||||
@@ -25,6 +25,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
||||
#include "networkd-ipv6ll.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-netlabel.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-neighbor.h"
|
||||
#include "networkd-nexthop.h"
|
||||
@@ -156,6 +157,7 @@ Address.AutoJoin, config_parse_address_flags,
|
||||
Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0
|
||||
Address.Scope, config_parse_address_scope, 0, 0
|
||||
Address.RouteMetric, config_parse_address_route_metric, 0, 0
|
||||
Address.NetLabel, config_parse_address_netlabel, 0, 0
|
||||
IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
|
||||
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
|
||||
Neighbor.Address, config_parse_neighbor_address, 0, 0
|
||||
@@ -243,6 +245,7 @@ DHCPv4.SendVendorOption, config_parse_dhcp_send_option,
|
||||
DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu)
|
||||
DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0
|
||||
DHCPv4.Use6RD, config_parse_bool, 0, offsetof(Network, dhcp_use_6rd)
|
||||
DHCPv4.NetLabel, config_parse_netlabel, 0, offsetof(Network, dhcp_netlabels)
|
||||
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
|
||||
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
|
||||
DHCPv6.UseDNS, config_parse_dhcp_use_dns, AF_INET6, 0
|
||||
@@ -260,6 +263,7 @@ DHCPv6.SendOption, config_parse_dhcp_send_option,
|
||||
DHCPv6.IAID, config_parse_iaid, AF_INET6, 0
|
||||
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid)
|
||||
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid)
|
||||
DHCPv6.NetLabel, config_parse_netlabel, 0, offsetof(Network, dhcp6_netlabels)
|
||||
IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway)
|
||||
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix)
|
||||
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
|
||||
@@ -277,6 +281,7 @@ IPv6AcceptRA.PrefixDenyList, config_parse_in_addr_prefixes,
|
||||
IPv6AcceptRA.RouteAllowList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_allow_listed_route_prefix)
|
||||
IPv6AcceptRA.RouteDenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_route_prefix)
|
||||
IPv6AcceptRA.Token, config_parse_address_generation_type, 0, offsetof(Network, ndisc_tokens)
|
||||
IPv6AcceptRA.NetLabel, config_parse_netlabel, 0, offsetof(Network, ndisc_netlabels)
|
||||
DHCPServer.ServerAddress, config_parse_dhcp_server_address, 0, 0
|
||||
DHCPServer.UplinkInterface, config_parse_uplink, 0, 0
|
||||
DHCPServer.RelayTarget, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_relay_target)
|
||||
@@ -343,6 +348,7 @@ DHCPPrefixDelegation.Assign, config_parse_bool,
|
||||
DHCPPrefixDelegation.ManageTemporaryAddress, config_parse_bool, 0, offsetof(Network, dhcp_pd_manage_temporary_address)
|
||||
DHCPPrefixDelegation.Token, config_parse_address_generation_type, 0, offsetof(Network, dhcp_pd_tokens)
|
||||
DHCPPrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric)
|
||||
DHCPPrefixDelegation.NetLabel, config_parse_netlabel, 0, offsetof(Network, dhcp_pd_netlabels)
|
||||
IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec)
|
||||
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
|
||||
IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
|
||||
|
||||
@@ -688,6 +688,8 @@ static Network *network_free(Network *network) {
|
||||
free(network->dhcp6_mudurl);
|
||||
strv_free(network->dhcp6_user_class);
|
||||
strv_free(network->dhcp6_vendor_class);
|
||||
set_free(network->dhcp_netlabels);
|
||||
set_free(network->dhcp6_netlabels);
|
||||
|
||||
strv_free(network->ntp);
|
||||
for (unsigned i = 0; i < network->n_dns; i++)
|
||||
@@ -754,6 +756,8 @@ static Network *network_free(Network *network) {
|
||||
ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
|
||||
set_free(network->dhcp_pd_tokens);
|
||||
set_free(network->ndisc_tokens);
|
||||
set_free(network->dhcp_pd_netlabels);
|
||||
set_free(network->ndisc_netlabels);
|
||||
|
||||
return mfree(network);
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ struct Network {
|
||||
Set *dhcp_request_options;
|
||||
OrderedHashmap *dhcp_client_send_options;
|
||||
OrderedHashmap *dhcp_client_send_vendor_options;
|
||||
Set *dhcp_netlabels;
|
||||
|
||||
/* DHCPv6 Client support */
|
||||
bool dhcp6_use_address;
|
||||
@@ -179,6 +180,7 @@ struct Network {
|
||||
OrderedHashmap *dhcp6_client_send_options;
|
||||
OrderedHashmap *dhcp6_client_send_vendor_options;
|
||||
Set *dhcp6_request_options;
|
||||
Set *dhcp6_netlabels;
|
||||
|
||||
/* DHCP Server Support */
|
||||
bool dhcp_server;
|
||||
@@ -235,6 +237,7 @@ struct Network {
|
||||
Set *dhcp_pd_tokens;
|
||||
int dhcp_pd_uplink_index;
|
||||
char *dhcp_pd_uplink_name;
|
||||
Set *dhcp_pd_netlabels;
|
||||
|
||||
/* Bridge Support */
|
||||
int use_bpdu;
|
||||
@@ -319,6 +322,7 @@ struct Network {
|
||||
Set *ndisc_deny_listed_route_prefix;
|
||||
Set *ndisc_allow_listed_route_prefix;
|
||||
Set *ndisc_tokens;
|
||||
Set *ndisc_netlabels;
|
||||
|
||||
/* LLDP support */
|
||||
LLDPMode lldp_mode; /* LLDP reception */
|
||||
|
||||
Reference in New Issue
Block a user