mirror of
https://github.com/systemd/systemd.git
synced 2025-01-21 22:04:01 +03:00
networkd/wireguard: support network.wireguard.* credentials
Closes #26702
This commit is contained in:
parent
d7d6195953
commit
fa724cd52c
@ -1891,13 +1891,22 @@
|
||||
<varlistentry>
|
||||
<term><varname>PrivateKey=</varname></term>
|
||||
<listitem>
|
||||
<para>The Base64 encoded private key for the interface. It can be
|
||||
generated using the <command>wg genkey</command> command
|
||||
<para>The Base64 encoded private key for the interface. It can be generated using
|
||||
the <command>wg genkey</command> command
|
||||
(see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
|
||||
This option or <varname>PrivateKeyFile=</varname> 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 <literal>root:systemd-network</literal>
|
||||
with a <literal>0640</literal> file mode.</para>
|
||||
Specially, if the specified key is prefixed with <literal>@</literal>, it is interpreted as
|
||||
the name of the credential from which the actual key shall be read. <command>systemd-networkd.service</command>
|
||||
automatically imports credentials matching <literal>network.wireguard.*</literal>. For more details
|
||||
on credentials, refer to
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
A private key is mandatory to use WireGuard. If not set, the credential
|
||||
<literal>network.wireguard.private.<replaceable>netdev</replaceable></literal> is used if exists.
|
||||
I.e. for <filename>50-foobar.netdev</filename>, <literal>network.wireguard.private.50-foobar</literal>
|
||||
is tried.</para>
|
||||
|
||||
<para>Note that because this information is secret, it's strongly recommended to use an (encrypted)
|
||||
credential. Alternatively, you may want to set the permissions of the .netdev file to be owned
|
||||
by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v237"/>
|
||||
</listitem>
|
||||
@ -1976,9 +1985,9 @@
|
||||
<listitem>
|
||||
<para>Sets a Base64 encoded public key calculated by <command>wg pubkey</command>
|
||||
(see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
|
||||
from a private key, and usually transmitted out of band to the
|
||||
author of the configuration file. This option is mandatory for this
|
||||
section.</para>
|
||||
from a private key, and usually transmitted out of band to the author of the configuration file.
|
||||
This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
|
||||
setting of the <option>[WireGuard]</option> section. This option is mandatory for this section.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v237"/>
|
||||
</listitem>
|
||||
@ -1986,14 +1995,15 @@
|
||||
<varlistentry>
|
||||
<term><varname>PresharedKey=</varname></term>
|
||||
<listitem>
|
||||
<para>Optional preshared key for the interface. It can be generated
|
||||
by the <command>wg genpsk</command> command. This option adds an
|
||||
additional layer of symmetric-key cryptography to be mixed into the
|
||||
already existing public-key cryptography, for post-quantum
|
||||
resistance.
|
||||
Note that because this information is secret, you may want to set
|
||||
the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
|
||||
with a <literal>0640</literal> file mode.</para>
|
||||
<para>Optional preshared key for the interface. It can be generated by the <command>wg genpsk</command>
|
||||
command. This option adds an additional layer of symmetric-key cryptography to be mixed into the
|
||||
already existing public-key cryptography, for post-quantum resistance.
|
||||
This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
|
||||
setting of the <option>[WireGuard]</option> section.</para>
|
||||
|
||||
<para>Note that because this information is secret, it's strongly recommended to use an (encrypted)
|
||||
credential. Alternatively, you may want to set the permissions of the .netdev file to be owned
|
||||
by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v237"/>
|
||||
</listitem>
|
||||
@ -2034,13 +2044,15 @@
|
||||
<varlistentry>
|
||||
<term><varname>Endpoint=</varname></term>
|
||||
<listitem>
|
||||
<para>Sets an endpoint IP address or hostname, followed by a colon, and then
|
||||
a port number. IPv6 address must be in the square brackets. For example,
|
||||
<literal>111.222.333.444:51820</literal> for IPv4 and <literal>[1111:2222::3333]:51820</literal>
|
||||
for IPv6 address. This endpoint will be updated automatically once to
|
||||
the most recent source IP address and port of correctly
|
||||
<para>Sets an endpoint IP address or hostname, followed by a colon, and then a port number.
|
||||
IPv6 address must be in the square brackets. For example, <literal>111.222.333.444:51820</literal>
|
||||
for IPv4 and <literal>[1111:2222::3333]:51820</literal> for IPv6 address. This endpoint will be
|
||||
updated automatically once to the most recent source IP address and port of correctly
|
||||
authenticated packets from the peer at configuration time.</para>
|
||||
|
||||
<para>This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
|
||||
setting of the <option>[WireGuard]</option> section.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v237"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -155,7 +155,21 @@
|
||||
|
||||
<para>Note that the resulting files are created world-readable, it's hence recommended to not include
|
||||
secrets in these credentials, but supply them via separate credentials directly to
|
||||
<filename>systemd-networkd.service</filename>.</para>
|
||||
<filename>systemd-networkd.service</filename>, e.g. <varname>network.wireguard.*</varname>
|
||||
as described below.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>network.wireguard.*</varname></term>
|
||||
<listitem>
|
||||
<para>Configures secrets for WireGuard netdevs. Read by
|
||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
For more information, refer to the <option>[WireGuard]</option> section of
|
||||
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||
</listitem>
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "sd-resolve.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "creds-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
@ -25,6 +26,7 @@
|
||||
#include "networkd-util.h"
|
||||
#include "parse-helpers.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "random-util.h"
|
||||
#include "resolve-private.h"
|
||||
#include "string-util.h"
|
||||
@ -480,6 +482,8 @@ static int wireguard_decode_key_and_warn(
|
||||
const char *lvalue) {
|
||||
|
||||
_cleanup_(erase_and_freep) void *key = NULL;
|
||||
_cleanup_(erase_and_freep) char *cred = NULL;
|
||||
const char *cred_name;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
@ -493,10 +497,22 @@ static int wireguard_decode_key_and_warn(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!streq(lvalue, "PublicKey"))
|
||||
cred_name = startswith(rvalue, "@");
|
||||
if (cred_name) {
|
||||
r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to read credential for wireguard key (%s=), ignoring assignment: %m",
|
||||
lvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else if (!streq(lvalue, "PublicKey"))
|
||||
(void) warn_file_is_world_accessible(filename, NULL, unit, line);
|
||||
|
||||
r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
|
||||
r = unbase64mem_full(cred ?: rvalue, SIZE_MAX, /* secure = */ true, &key, &len);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
@ -721,23 +737,39 @@ int config_parse_wireguard_endpoint(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
assert(filename);
|
||||
assert(rvalue);
|
||||
assert(userdata);
|
||||
|
||||
Wireguard *w = WIREGUARD(userdata);
|
||||
_cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
|
||||
_cleanup_free_ char *host = NULL;
|
||||
union in_addr_union addr;
|
||||
const char *p;
|
||||
_cleanup_free_ char *cred = NULL;
|
||||
const char *cred_name, *endpoint;
|
||||
uint16_t port;
|
||||
int family, r;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(rvalue);
|
||||
|
||||
r = wireguard_peer_new_static(w, filename, section_line, &peer);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = in_addr_port_ifindex_name_from_string_auto(rvalue, &family, &addr, &port, NULL, NULL);
|
||||
cred_name = startswith(rvalue, "@");
|
||||
if (cred_name) {
|
||||
r = read_credential(cred_name, (void**) &cred, /* ret_size = */ NULL);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to read credential for wireguard endpoint, ignoring assignment: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
endpoint = strstrip(cred);
|
||||
} else
|
||||
endpoint = rvalue;
|
||||
|
||||
union in_addr_union addr;
|
||||
int family;
|
||||
|
||||
r = in_addr_port_ifindex_name_from_string_auto(endpoint, &family, &addr, &port, NULL, NULL);
|
||||
if (r >= 0) {
|
||||
if (family == AF_INET)
|
||||
peer->endpoint.in = (struct sockaddr_in) {
|
||||
@ -761,17 +793,23 @@ int config_parse_wireguard_endpoint(
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = strrchr(rvalue, ':');
|
||||
_cleanup_free_ char *host = NULL;
|
||||
const char *p;
|
||||
|
||||
p = strrchr(endpoint, ':');
|
||||
if (!p) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Unable to find port of endpoint, ignoring assignment: %s",
|
||||
rvalue);
|
||||
rvalue); /* We log the original assignment instead of resolved credential here,
|
||||
as the latter might be previously encrypted and we'd expose them in
|
||||
unprotected logs otherwise. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
host = strndup(rvalue, p - rvalue);
|
||||
host = strndup(endpoint, p - endpoint);
|
||||
if (!host)
|
||||
return log_oom();
|
||||
p++;
|
||||
|
||||
if (!dns_name_is_valid(host)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
@ -780,7 +818,6 @@ int config_parse_wireguard_endpoint(
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
r = parse_ip_port(p, &port);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
@ -1078,6 +1115,55 @@ static int wireguard_peer_verify(WireguardPeer *peer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wireguard_read_default_key_cred(NetDev *netdev, const char *filename) {
|
||||
Wireguard *w = WIREGUARD(netdev);
|
||||
_cleanup_free_ char *config_name = NULL;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
|
||||
r = path_extract_filename(filename, &config_name);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r,
|
||||
"%s: Failed to extract config name, ignoring network device: %m",
|
||||
filename);
|
||||
|
||||
char *p = endswith(config_name, ".netdev");
|
||||
if (!p)
|
||||
/* Fuzzer run? Then we just ignore this device. */
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Invalid netdev config name, refusing default key lookup.",
|
||||
filename);
|
||||
*p = '\0';
|
||||
|
||||
_cleanup_(erase_and_freep) char *cred = NULL;
|
||||
|
||||
r = read_credential(strjoina("network.wireguard.private.", config_name), (void**) &cred, /* ret_size = */ NULL);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r,
|
||||
"%s: No private key specified and default key isn't available, "
|
||||
"ignoring network device: %m",
|
||||
filename);
|
||||
|
||||
_cleanup_(erase_and_freep) void *key = NULL;
|
||||
size_t len;
|
||||
|
||||
r = unbase64mem_full(cred, SIZE_MAX, /* secure = */ true, &key, &len);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r,
|
||||
"%s: No private key specified and default key cannot be parsed, "
|
||||
"ignoring network device: %m",
|
||||
filename);
|
||||
if (len != WG_KEY_LEN)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: No private key specified and default key is invalid. "
|
||||
"Ignoring network device.",
|
||||
filename);
|
||||
|
||||
memcpy(w->private_key, key, WG_KEY_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wireguard_verify(NetDev *netdev, const char *filename) {
|
||||
Wireguard *w = WIREGUARD(netdev);
|
||||
int r;
|
||||
@ -1088,10 +1174,11 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
|
||||
"Failed to read private key from %s. Ignoring network device.",
|
||||
w->private_key_file);
|
||||
|
||||
if (eqzero(w->private_key))
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Missing PrivateKey= or PrivateKeyFile=, "
|
||||
"Ignoring network device.", filename);
|
||||
if (eqzero(w->private_key)) {
|
||||
r = wireguard_read_default_key_cred(netdev, filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(peers, peer, w->peers) {
|
||||
if (wireguard_peer_verify(peer) < 0) {
|
||||
|
@ -0,0 +1 @@
|
||||
192.168.27.3:51820
|
@ -0,0 +1 @@
|
||||
EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
|
@ -4,6 +4,6 @@ Name=wg97
|
||||
Kind=wireguard
|
||||
|
||||
[WireGuard]
|
||||
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
|
||||
#PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
|
||||
ListenPort=51821
|
||||
FwMark=1235
|
||||
|
@ -0,0 +1 @@
|
||||
6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
|
@ -13,8 +13,8 @@ RouteMetric=456
|
||||
[WireGuardPeer]
|
||||
PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
|
||||
AllowedIPs=fd31:bf08:57cb::/48,192.168.26.3/24
|
||||
#Endpoint=wireguard.example.com:51820
|
||||
Endpoint=192.168.27.3:51820
|
||||
#Endpoint=192.168.27.3:51820
|
||||
Endpoint=@network.wireguard.peer0.endpoint
|
||||
PresharedKey=IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=
|
||||
PersistentKeepalive=20
|
||||
RouteTable=1234
|
||||
|
@ -1,5 +1,5 @@
|
||||
[WireGuardPeer]
|
||||
PublicKey=9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=
|
||||
PresharedKey=6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
|
||||
PresharedKey=@network.wireguard.peer2.psk
|
||||
|
||||
AllowedIPs=192.168.124.3
|
||||
|
@ -27,6 +27,7 @@ network_unit_dir = '/run/systemd/network'
|
||||
networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d'
|
||||
networkd_ci_temp_dir = '/run/networkd-ci'
|
||||
udev_rules_dir = '/run/udev/rules.d'
|
||||
credstore_dir = '/run/credstore'
|
||||
|
||||
dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid'
|
||||
dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log'
|
||||
@ -298,6 +299,11 @@ def copy_network_unit(*units, copy_dropins=True):
|
||||
if has_link:
|
||||
udev_reload()
|
||||
|
||||
def copy_credential(src, target):
|
||||
mkdir_p(credstore_dir)
|
||||
cp(os.path.join(networkd_ci_temp_dir, src),
|
||||
os.path.join(credstore_dir, target))
|
||||
|
||||
def remove_network_unit(*units):
|
||||
"""
|
||||
Remove previously copied unit files from the testbed.
|
||||
@ -1784,6 +1790,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
|
||||
|
||||
@expectedFailureIfModuleIsNotAvailable('wireguard')
|
||||
def test_wireguard(self):
|
||||
copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
|
||||
copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
|
||||
copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
|
||||
|
||||
copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
|
||||
'25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
|
||||
'25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
|
||||
|
@ -50,6 +50,7 @@ SystemCallErrorNumber=EPERM
|
||||
SystemCallFilter=@system-service
|
||||
Type=notify-reload
|
||||
User=systemd-network
|
||||
ImportCredential=network.wireguard.*
|
||||
{{SERVICE_WATCHDOG}}
|
||||
|
||||
[Install]
|
||||
|
Loading…
x
Reference in New Issue
Block a user