diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 69e7d53fc7d..9d8f045da6e 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -1113,12 +1113,24 @@ The Base64 encoded private key for the interface. It can be generated using the wg genkey command (see wg8). - This option is mandatory to use WireGuard. + This option or PrivateKeyFile= is mandatory to use WireGuard. Note that because this information is secret, you may want to set the permissions of the .netdev file to be owned by root:systemd-network with a 0640 file mode. + + PrivateKeyFile= + + Takes a absolute path to a file which contains the Base64 encoded private key for the interface. + If both PrivateKey= and PrivateKeyFile= are specified, and if + the file specified in PrivateKeyFile= contains valid wireguard key, then + the key provided by PrivateKey= is ignored. + Note that the file must be readable by the user systemd-network, so it + should be, e.g., owned by root:systemd-network with a + 0640 file mode. + + ListenPort= diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index f7ca98fa467..29bdc65a21f 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -167,6 +167,7 @@ VRF.Table, config_parse_uint32, 0, WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port) WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0 +WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0 WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0 WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0 WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0 diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index c5c60c1752f..eefb543c4d3 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -11,12 +11,14 @@ #include "alloc-util.h" #include "event-util.h" #include "fd-util.h" +#include "fileio.h" #include "hexdecoct.h" #include "netlink-util.h" #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-util.h" #include "parse-util.h" +#include "path-util.h" #include "resolve-private.h" #include "string-util.h" #include "strv.h" @@ -529,6 +531,40 @@ int config_parse_wireguard_private_key(const char *unit, } +int config_parse_wireguard_private_key_file( + 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) { + + _cleanup_free_ char *path = NULL; + Wireguard *w; + + assert(data); + w = WIREGUARD(data); + assert(w); + + if (isempty(rvalue)) { + w->private_key_file = mfree(w->private_key_file); + return 0; + } + + path = strdup(rvalue); + if (!path) + return log_oom(); + + if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0) + return 0; + + return free_and_replace(w->private_key_file, path); +} + int config_parse_wireguard_preshared_key(const char *unit, const char *filename, unsigned line, @@ -807,11 +843,50 @@ static void wireguard_done(NetDev *netdev) { sd_event_source_unref(w->resolve_retry_event_source); + free(w->private_key_file); + hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free); set_free(w->peers_with_unresolved_endpoint); set_free(w->peers_with_failed_endpoint); } +static int wireguard_read_private_key_file(Wireguard *w, bool fatal) { + _cleanup_free_ char *contents = NULL; + _cleanup_free_ void *key = NULL; + size_t size, key_len; + NetDev *netdev; + int level, r; + + assert(w); + + netdev = NETDEV(w); + + if (!w->private_key_file) + return 0; + + level = fatal ? LOG_ERR : LOG_INFO; + + r = read_full_file(w->private_key_file, &contents, &size); + if (r < 0) + return log_netdev_full(netdev, level, r, + "Failed to read private key from '%s'%s: %m", + w->private_key_file, fatal ? "" : ", ignoring"); + + r = unbase64mem(contents, size, &key, &key_len); + if (r < 0) + return log_netdev_full(netdev, level, r, + "Failed to decode private key%s: %m", + fatal ? "" : ", ignoring"); + + if (key_len != WG_KEY_LEN) + return log_netdev_full(netdev, level, SYNTHETIC_ERRNO(EINVAL), + "Wireguard private key has invalid length (%zu bytes)%s: %m", + key_len, fatal ? "" : ", ignoring"); + + memcpy(w->private_key, key, WG_KEY_LEN); + return 0; +} + static int wireguard_peer_verify(WireguardPeer *peer) { NetDev *netdev = NETDEV(peer->wireguard); @@ -830,16 +905,23 @@ static int wireguard_peer_verify(WireguardPeer *peer) { static int wireguard_verify(NetDev *netdev, const char *filename) { WireguardPeer *peer, *peer_next; Wireguard *w; + bool empty; + int r; assert(netdev); w = WIREGUARD(netdev); assert(w); - if (eqzero(w->private_key)) + empty = eqzero(w->private_key); + if (empty && !w->private_key_file) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "%s: Missing PrivateKey= or PrivateKeyFile=, ignoring.", filename); + r = wireguard_read_private_key_file(w, empty); + if (r < 0 && empty) + return r; + LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers) if (wireguard_peer_verify(peer) < 0) wireguard_peer_free(peer); diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 65e9ac24b9a..6cf6eec14db 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -38,6 +38,7 @@ struct Wireguard { uint32_t flags; uint8_t private_key[WG_KEY_LEN]; + char *private_key_file; uint16_t port; uint32_t fwmark; @@ -60,5 +61,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_listen_port); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_public_key); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key_file); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive); diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index cd7c3aadedb..419ccda7dcc 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -9,6 +9,7 @@ Mode= [WireGuard] ListenPort= PrivateKey= +PrivateKeyFile= FwMark= [MACVTAP] Mode=