From b4dde691ec297b79bc1efc2d87533a41d909f8a7 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 5 Jun 2025 17:53:50 +0200 Subject: [PATCH] nmcli: add support for managing wireguard peers Even if WireGuard is supported since long time in NetworkManager, it is still not possible to manage the list of peers via nmcli. The reason is that in the past we wanted to introduce a special syntax that would allow to manage the peer list more easily. However, this requires heavy changes to the nmcli output formatting code, and so it never happened. Since perfection is the enemy of good, abandon the idea of a custom handling of peers and treat them as any other composite property. The property is named "wireguard.peers" and exposes the peers indexed by public key, with optional attributes. Example: $ nmcli connection modify wg0 wireguard.peers "8Wgc1a0jJX3rQULwD5NFFLKrKQnbOnTiaNoerLneG1o= preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA= allowed-ips=0.0.0.0/0 persistent-keepalive=10" $ nmcli connection modify wg0 +wireguard.peers "fd2NSxUjkaR/Jft15+gpXU13hKSyZLoe4cp+g+feBCc= allowed-ips=192.168.40.0/24 endpoint=172.25.10.1:8888" $ nmcli -g wireguard.peers connection show wg0 8Wgc1a0jJX3rQULwD5NFFLKrKQnbOnTiaNoerLneG1o= allowed-ips=0.0.0.0/0 persistent-keepalive=10, fd2NSxUjkaR/Jft15+gpXU13hKSyZLoe4cp+g+feBCc= allowed-ips=192.168.40.0/24 endpoint=172.25.10.1\:8888 $ nmcli connection modify wg0 -wireguard.peers 8Wgc1a0jJX3rQULwD5NFFLKrKQnbOnTiaNoerLneG1o= $ nmcli -g wireguard.peers connection show wg0 fd2NSxUjkaR/Jft15+gpXU13hKSyZLoe4cp+g+feBCc= allowed-ips=192.168.40.0/24 endpoint=172.25.10.1\:8888 --- NEWS | 1 + .../tests/test-libnm-client-aux.c | 222 ++++++++++++++++++ src/libnm-core-aux-extern/nm-libnm-core-aux.c | 175 ++++++++++++++ src/libnm-core-aux-extern/nm-libnm-core-aux.h | 6 +- src/libnm-core-impl/nm-setting-wireguard.c | 67 ++++++ src/libnmc-setting/nm-meta-setting-desc.c | 59 +++++ src/libnmc-setting/settings-docs.h.in | 1 + .../gen-metadata-nm-settings-nmcli.xml.in | 3 + 8 files changed, 533 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 901a8bb9c..9a248c702 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ subject to change and not guaranteed to be compatible with the later release. USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE! +* nmcli now supports viewing and managing WireGuard peers. ============================================= NetworkManager-1.54 diff --git a/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c b/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c index f727b3d93..6a25b652d 100644 --- a/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c +++ b/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c @@ -234,6 +234,227 @@ test_team_link_watcher_tofro_string(void) NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE); } +static void +test_wireguard_peer(void) +{ + guint i; + struct { + const char *input; + const char *canonical; /* canonical string representation */ + + gboolean invalid; + const char *pubkey; + const char *endpoint; + guint16 keepalive; + guint num_allowed_ips; + const char *allowed_ips[2]; + const char *psk; + int psk_flags; + } tests[] = {{ + /* Public key only */ + .input = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=", + .canonical = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=", + .pubkey = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=", + }, + { + /* IPv4 endpoint */ + .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=" + " endpoint=1.2.3.4:5555", + .canonical = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=" + " endpoint=1.2.3.4:5555", + .pubkey = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=", + .endpoint = "1.2.3.4:5555", + }, + { + /* IPv6 endpoint */ + .input = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s=" + " endpoint=[fd01:db8::1]:8080", + .canonical = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s=" + " endpoint=[fd01:db8::1]:8080", + .pubkey = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s=", + .endpoint = "[fd01:db8::1]:8080", + }, + { + /* IPv6 endpoint, without brackets */ + .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=" + " endpoint=fd01::12:8080", + .canonical = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=" + " endpoint=fd01::12:8080", + .pubkey = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=", + .endpoint = "fd01::12:8080", + }, + { + /* Single IPv4 allowed-ip */ + .input = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY=" + " allowed-ips=172.16.0.0/16", + .canonical = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY=" + " allowed-ips=172.16.0.0/16", + .pubkey = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY=", + .num_allowed_ips = 1, + .allowed_ips = {"172.16.0.0/16"}, + }, + { + /* Multiple allowed-ips */ + .input = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ=" + " allowed-ips=192.168.2.0/24;2001:db8:a::/48", + .canonical = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ=" + " allowed-ips=192.168.2.0/24;2001:db8:a::/48", + .pubkey = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ=", + .num_allowed_ips = 2, + .allowed_ips = {"192.168.2.0/24", "2001:db8:a::/48"}, + }, + { + /* Persistent-keepalive */ + .input = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM=" + " persistent-keepalive=25", + .canonical = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM=" + " persistent-keepalive=25", + .pubkey = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM=", + .keepalive = 25, + }, + { + /* Preshared-key without flags (should default to 0) */ + .input = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=" + " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=", + .canonical = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=" + " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=" + " preshared-key-flags=0", + .pubkey = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=", + .psk = "16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=", + .psk_flags = 0, + }, + { + /* Preshared-key flags as string */ + .input = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=" + " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=" + " preshared-key-flags=not-saved", + .canonical = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=" + " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=" + " preshared-key-flags=2", + .pubkey = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=", + .psk = "16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=", + .psk_flags = 2, + }, + { + /* Non-canonical order and extra whitespaces */ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY=" + " preshared-key=EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc=" + " endpoint=vpn.example.com:51820 " + " preshared-key-flags=1" + " persistent-keepalive=45" + " allowed-ips=0.0.0.0/0;::/0", + .canonical = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY=" + " allowed-ips=0.0.0.0/0;::/0" + " endpoint=vpn.example.com:51820" + " persistent-keepalive=45" + " preshared-key=EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc=" + " preshared-key-flags=1", + .pubkey = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY=", + .endpoint = "vpn.example.com:51820", + .keepalive = 45, + .num_allowed_ips = 2, + .allowed_ips = {"0.0.0.0/0", "::/0"}, + .psk = "EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc=", + .psk_flags = 1, + }, + { + /* Empty string */ + .input = "", + .invalid = TRUE, + }, + { + /* Invalid public key*/ + .input = "aaaaaaaaaaaaaaaaaaaaaaa=", + .invalid = TRUE, + }, + { + /* Missing value*/ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= " + "persistent-keepalive=", + .invalid = TRUE, + }, + { + /* Unknown attribute */ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= " + "persistent-keepalive=12 foobarness=13", + .invalid = TRUE, + }, + { + /* Invalid IPv4 allowed-ip*/ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= " + "allowed-ips=192.168.10.256/32", + .invalid = TRUE, + }, + { + /* Invalid IPv6 allowed-ip */ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= " + "allowed-ips=fd01::1::3/64", + .invalid = TRUE, + }, + { + /* Endpoint with no port */ + .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=" + " endpoint=1.2.3.4", + .invalid = TRUE, + }, + { + /* Invalid endpoint */ + .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=" + " endpoint=1.2.3.5.6", + .invalid = TRUE, + }, + { + /* Invalid persistent-keepalive */ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= " + "persistent-keepalive=yes", + .invalid = TRUE, + }, + { + /* Invalid PSK */ + .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY=" + " preshared-key=pskpskpskpskpskpskpskpskpskpskpskpsk", + .invalid = TRUE, + }}; + + for (i = 0; i < G_N_ELEMENTS(tests); i++) { + nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; + gs_free_error GError *error = NULL; + gs_free char *newstr = NULL; + guint j; + + peer = _nm_utils_wireguard_peer_from_string(tests[i].input, &error); + if (tests[i].invalid) { + g_assert(!peer); + g_assert(error); + continue; + } + g_assert_no_error(error); + g_assert_nonnull(peer); + + newstr = _nm_utils_wireguard_peer_to_string(peer); + g_assert_nonnull(newstr); + g_assert_cmpstr(tests[i].canonical, ==, newstr); + + g_assert_cmpstr(tests[i].pubkey, ==, nm_wireguard_peer_get_public_key(peer)); + g_assert_cmpstr(tests[i].endpoint, ==, nm_wireguard_peer_get_endpoint(peer)); + + g_assert_cmpint(tests[i].num_allowed_ips, ==, nm_wireguard_peer_get_allowed_ips_len(peer)); + for (j = 0; j < tests[i].num_allowed_ips; j++) { + g_assert_cmpstr(tests[i].allowed_ips[j], + ==, + nm_wireguard_peer_get_allowed_ip(peer, j, NULL)); + } + + g_assert_cmpint(tests[i].keepalive, ==, nm_wireguard_peer_get_persistent_keepalive(peer)); + g_assert_cmpstr(tests[i].psk, ==, nm_wireguard_peer_get_preshared_key(peer)); + if (tests[i].psk) { + g_assert_cmpint(tests[i].psk_flags, + ==, + nm_wireguard_peer_get_preshared_key_flags(peer)); + } + } +} + /*****************************************************************************/ NMTST_DEFINE(); @@ -245,6 +466,7 @@ main(int argc, char **argv) g_test_add_func("/libnm-core-aux/test_team_link_watcher_tofro_string", test_team_link_watcher_tofro_string); + g_test_add_func("/libnm-core-aux/test-wireguard-peer", test_wireguard_peer); return g_test_run(); } diff --git a/src/libnm-core-aux-extern/nm-libnm-core-aux.c b/src/libnm-core-aux-extern/nm-libnm-core-aux.c index dfc70e35b..b12000fc1 100644 --- a/src/libnm-core-aux-extern/nm-libnm-core-aux.c +++ b/src/libnm-core-aux-extern/nm-libnm-core-aux.c @@ -7,6 +7,7 @@ #include "nm-libnm-core-aux.h" +#include "nm-errors.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-glib-aux/nm-str-buf.h" @@ -475,3 +476,177 @@ _nm_ip_route_to_string(NMIPRoute *route, NMStrBuf *strbuf) nm_str_buf_append_printf(strbuf, " metric %" G_GINT64_FORMAT, metric); } } + +/*****************************************************************************/ + +char * +_nm_utils_wireguard_peer_to_string(NMWireGuardPeer *peer) +{ + GString *str; + const char *endpoint; + const char *psk; + guint16 keepalive; + guint i; + guint len; + + g_return_val_if_fail(peer, ""); + + nm_assert(nm_wireguard_peer_is_valid(peer, TRUE, TRUE, NULL)); + + str = g_string_new(""); + g_string_append(str, nm_wireguard_peer_get_public_key(peer)); + + len = nm_wireguard_peer_get_allowed_ips_len(peer); + if (len > 0) { + g_string_append(str, " allowed-ips="); + for (i = 0; i < len; i++) { + g_string_append(str, nm_wireguard_peer_get_allowed_ip(peer, i, NULL)); + if (i < len - 1) + g_string_append(str, ";"); + } + } + + endpoint = nm_wireguard_peer_get_endpoint(peer); + if (endpoint) { + g_string_append_printf(str, " endpoint=%s", endpoint); + } + + keepalive = nm_wireguard_peer_get_persistent_keepalive(peer); + if (keepalive != 0) { + g_string_append_printf(str, " persistent-keepalive=%hu", keepalive); + } + + psk = nm_wireguard_peer_get_preshared_key(peer); + if (psk) { + g_string_append_printf(str, " preshared-key=%s", psk); + g_string_append_printf(str, + " preshared-key-flags=%u", + (guint) nm_wireguard_peer_get_preshared_key_flags(peer)); + } + + return g_string_free(str, FALSE); +} + +NMWireGuardPeer * +_nm_utils_wireguard_peer_from_string(const char *str, GError **error) +{ + nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; + gs_strfreev char **tokens = NULL; + gboolean has_psk = FALSE; + gboolean has_psk_flags = FALSE; + char *value; + guint i; + + peer = nm_wireguard_peer_new(); + + tokens = g_strsplit_set(str, " ", 0); + for (i = 0; tokens[i]; i++) { + if (i == 0) { + if (!nm_wireguard_peer_set_public_key(peer, tokens[i], FALSE)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid public key '%s'", + tokens[i]); + return NULL; + } + continue; + } + + if (tokens[i][0] == '\0') + continue; + + value = strchr(tokens[i], '='); + if (!value || value[1] == '\0') { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "attribute without value '%s'", + tokens[i]); + return NULL; + } + + *value = '\0'; + value++; + + if (nm_streq(tokens[i], "allowed-ips")) { + gs_strfreev char **ips = NULL; + guint j; + + ips = g_strsplit_set(value, ";", 0); + for (j = 0; ips[j]; j++) { + if (!nm_wireguard_peer_append_allowed_ip(peer, ips[j], FALSE)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid allowed-ip '%s'", + ips[j]); + return NULL; + } + } + } else if (nm_streq(tokens[i], "endpoint")) { + if (!nm_wireguard_peer_set_endpoint(peer, value, FALSE)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid endpoint '%s'", + value); + return NULL; + } + } else if (nm_streq(tokens[i], "persistent-keepalive")) { + gint64 keepalive; + + keepalive = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT16, -1); + if (keepalive == -1) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid persistent-keepalive value '%s'", + value); + return NULL; + } + nm_wireguard_peer_set_persistent_keepalive(peer, (guint16) keepalive); + } else if (nm_streq(tokens[i], "preshared-key")) { + if (!nm_wireguard_peer_set_preshared_key(peer, value, FALSE)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid preshared-key '%s'", + value); + return NULL; + } + has_psk = TRUE; + } else if (nm_streq(tokens[i], "preshared-key-flags")) { + int flags; + + if (!nm_utils_enum_from_str(NM_TYPE_SETTING_SECRET_FLAGS, value, &flags, NULL)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid preshared-key-flags '%s'", + value); + return NULL; + } + nm_wireguard_peer_set_preshared_key_flags(peer, (NMSettingSecretFlags) flags); + has_psk_flags = TRUE; + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + "invalid attribute '%s'", + tokens[i]); + return NULL; + } + } + + if (has_psk && !has_psk_flags) { + /* The flags are NOT_REQUIRED by default. With this flag, the PSK would not + * be saved by default, unless the user explicitly sets a different value. */ + nm_wireguard_peer_set_preshared_key_flags(peer, NM_SETTING_SECRET_FLAG_NONE); + } + + if (!nm_wireguard_peer_is_valid(peer, TRUE, TRUE, error)) + return NULL; + + return g_steal_pointer(&peer); +} diff --git a/src/libnm-core-aux-extern/nm-libnm-core-aux.h b/src/libnm-core-aux-extern/nm-libnm-core-aux.h index e45e98ab2..aa7ea631e 100644 --- a/src/libnm-core-aux-extern/nm-libnm-core-aux.h +++ b/src/libnm-core-aux-extern/nm-libnm-core-aux.h @@ -6,8 +6,9 @@ #ifndef __NM_LIBNM_CORE_AUX_H__ #define __NM_LIBNM_CORE_AUX_H__ -#include "nm-setting-team.h" #include "nm-setting-ip-config.h" +#include "nm-setting-team.h" +#include "nm-setting-wireguard.h" typedef enum { NM_TEAM_LINK_WATCHER_TYPE_NONE = 0, @@ -39,4 +40,7 @@ void _nm_ip_route_to_string(NMIPRoute *route, struct _NMStrBuf *strbuf); NMTeamLinkWatcher *nm_utils_team_link_watcher_from_string(const char *str, GError **error); +char *_nm_utils_wireguard_peer_to_string(NMWireGuardPeer *peer); +NMWireGuardPeer *_nm_utils_wireguard_peer_from_string(const char *str, GError **error); + #endif /* __NM_LIBNM_CORE_AUX_H__ */ diff --git a/src/libnm-core-impl/nm-setting-wireguard.c b/src/libnm-core-impl/nm-setting-wireguard.c index 668af1f6e..a03090865 100644 --- a/src/libnm-core-impl/nm-setting-wireguard.c +++ b/src/libnm-core-impl/nm-setting-wireguard.c @@ -2515,6 +2515,73 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass) NMSettingWireGuard, _priv.ip6_auto_default_route); + /* ---nmcli--- + * property: peers + * format: a comma-separated list of WireGuard peers + * description: + * A comma-separated list of WireGuard peers. Each peer has the following syntax: + * + * PUBLIC_KEY [ATTRIBUTE=VALUE [ATTRIBUTE=VALUE]...] + * + * The supported attributes are: endpoint, allowed-ips, persistent-keepalive, + * preshared-key, preshared-key-flags. + * description-docbook: + * + * A comma-separated list of WireGuard peers. Each peer has the following syntax: + * + * + * + * public-key + * [attribute=value + * [attribute=value]...] + * + * + * + * The public key is required and must be encoded as base64; it can be + * calculated by running wg pubkey on the private key, + * and it is usually transmitted out of band to the author of the configuration + * file. + * + * + * The supported attributes are: + * + * + * endpoint + * An endpoint IP or hostname, followed by a colon, and then + * a port number. + * + * + * + * + * + * + * + * allowed-ips + * A semicolon-separated list of IP (v4 or v6) addresses + * with CIDR masks from which incoming traffic for this peer is allowed + * and to which outgoing traffic for this peer is directed + * + * + * + * persistent-keepalive + * An interval in seconds, between 1 and 65535, of + * how often to send an authenticated empty packet to the peer for the + * purpose of keeping a stateful firewall or NAT mapping valid persistently. + * + * + * + * preshared-key + * A base64 preshared key generated by "wg genpsk". Optional, + * and may be omitted. + * + * + * preshared-key-flags + * The secret flags for the preshared-key. + * + * + * + * ---end--- + */ /* ---dbus--- * property: peers * format: array of 'a{sv}' diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c index 9b11cd4a1..b025093ad 100644 --- a/src/libnmc-setting/nm-meta-setting-desc.c +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -4156,6 +4156,50 @@ _optionlist_set_fcn_vpn_secrets(NMSetting *setting, return TRUE; } +static void +_objlist_obj_to_str_fcn_wireguard_peers(NMMetaAccessorGetType get_type, + NMSetting *setting, + guint idx, + GString *str) +{ + NMWireGuardPeer *peer; + gs_free char *peer_str = NULL; + + peer = nm_setting_wireguard_get_peer(NM_SETTING_WIREGUARD(setting), idx); + peer_str = _nm_utils_wireguard_peer_to_string(peer); + g_string_append(str, peer_str); +} + +static gboolean +_objlist_set_fcn_wireguard_peers(NMSetting *setting, + gboolean do_add, + const char *value, + GError **error) +{ + NMSettingWireGuard *s_wg = NM_SETTING_WIREGUARD(setting); + nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; + + peer = _nm_utils_wireguard_peer_from_string(value, error); + if (!peer) + return FALSE; + + if (do_add) { + nm_setting_wireguard_append_peer(s_wg, peer); + } else { + NMWireGuardPeer *match; + guint idx; + + match = nm_setting_wireguard_get_peer_by_public_key(s_wg, + nm_wireguard_peer_get_public_key(peer), + &idx); + if (match) { + nm_setting_wireguard_remove_peer(s_wg, idx); + } + } + + return TRUE; +} + static gboolean _set_fcn_wired_s390_subchannels(ARGS_SET_FCN) { @@ -8423,6 +8467,21 @@ static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = { PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE, .property_type = &_pt_gobject_ternary, ), + + PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PEERS, + .property_type = &_pt_objlist, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + PROPERTY_TYP_DATA_SUBTYPE (objlist, + .get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingWireGuard, nm_setting_wireguard_get_peers_len), + .clear_all_fcn = (void (*) (NMSetting *))(void (*)(void)) nm_setting_wireguard_clear_peers, + .obj_to_str_fcn = _objlist_obj_to_str_fcn_wireguard_peers, + .set_fcn = _objlist_set_fcn_wireguard_peers, + .remove_by_idx_fcn_u = (void (*) (NMSetting *, guint idx))(void (*)(void)) nm_setting_wireguard_remove_peer, + .strsplit_plain = TRUE, + ), + ), + ), + NULL }; diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in index 814ca1929..8678dc64e 100644 --- a/src/libnmc-setting/settings-docs.h.in +++ b/src/libnmc-setting/settings-docs.h.in @@ -443,6 +443,7 @@ #define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.") #define DESCRIBE_DOC_NM_SETTING_WIREGUARD_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation.") #define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEER_ROUTES N_("Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. Note that if the peer's AllowedIPs is \"0.0.0.0/0\" or \"::/0\" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically.") +#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEERS N_("A comma-separated list of WireGuard peers. Each peer has the following syntax: PUBLIC_KEY [ATTRIBUTE=VALUE [ATTRIBUTE=VALUE]...] The supported attributes are: endpoint, allowed-ips, persistent-keepalive, preshared-key, preshared-key-flags.") #define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY N_("The 256 bit private-key in base64 encoding.") #define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS N_("Flags indicating how to handle the \"private-key\" property.") #define DESCRIBE_DOC_NM_SETTING_WIRELESS_AP_ISOLATION N_("Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from \"default\" (-1) only when the interface is configured in AP mode. If set to \"true\" (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to \"false\" (0), devices can talk to each other. When set to \"default\" (-1), the global default is used; in case the global default is unspecified it is assumed to be \"false\" (0).") diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in index 155c413cb..957480aea 100644 --- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in +++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in @@ -2320,6 +2320,9 @@ nmcli-description="Like ip4-auto-default-route, but for the IPv6 default route." format="ternary" values="true/yes/on, false/no/off, default/unknown" /> +