public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Hanreich <s.hanreich@proxmox.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>,
	Christoph Heiss <c.heiss@proxmox.com>
Subject: Re: [pve-devel] [PATCH proxmox 06/11] wireguard: init configuration support crate
Date: Fri, 13 Feb 2026 11:13:28 +0100	[thread overview]
Message-ID: <0c44a626-af78-4bb9-89a3-e5e5c6f6fd1b@proxmox.com> (raw)
In-Reply-To: <20260116153317.1146323-7-c.heiss@proxmox.com>

comments inline

On 1/16/26 4:33 PM, Christoph Heiss wrote:

[snip]

> +impl PrivateKey {
> +    /// Length of the raw private key data in bytes.
> +    pub const RAW_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
> +
> +    /// Generates a new private key suitable for use with WireGuard.
> +    pub fn generate() -> Result<Self, Error> {
> +        generate_key().map(Self)
> +    }
> +
> +    /// Calculates the public key from the private key.
> +    pub fn public_key(&self) -> PublicKey {
> +        PublicKey(
> +            ed25519_dalek::SigningKey::from_bytes(&self.0)
> +                .verifying_key()
> +                .to_bytes(),
> +        )
> +    }
> +
> +    /// Returns the raw private key material.
> +    #[must_use]
> +    pub fn raw(&self) -> &ed25519_dalek::SecretKey {
> +        &self.0
> +    }

might be better to implement AsRef instead for ergonomics?

> +    /// Builds a new [`PrivateKey`] from raw key material.
> +    #[must_use]
> +    pub fn from_raw(data: ed25519_dalek::SecretKey) -> Self {
> +        // [`SigningKey`] takes care of correct key clamping.
> +        Self(SigningKey::from(&data).to_bytes())
> +    }
> +}
> +
> +impl From<ed25519_dalek::SecretKey> for PrivateKey {
> +    fn from(value: ed25519_dalek::SecretKey) -> Self {
> +        Self(value)
> +    }
> +}
> +
> +/// Preshared key between two WireGuard peers.
> +#[derive(Clone, Deserialize, Serialize)]
> +#[serde(transparent)]
> +pub struct PresharedKey(
> +    #[serde(with = "proxmox_serde::byte_array_as_base64")] ed25519_dalek::SecretKey,
> +);
> +
> +impl fmt::Debug for PresharedKey {
> +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> +        write!(f, "<preshared-key>")
> +    }
> +}
> +
> +impl PresharedKey {
> +    /// Length of the raw private key data in bytes.
> +    pub const RAW_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
> +
> +    /// Generates a new preshared key suitable for use with WireGuard.
> +    pub fn generate() -> Result<Self, Error> {
> +        generate_key().map(Self)
> +    }
> +
> +    /// Returns the raw private key material.
> +    #[must_use]
> +    pub fn raw(&self) -> &ed25519_dalek::SecretKey {
> +        &self.0
> +    }
> +

here too: might be better to implement AsRef instead for ergonomics?

> +    /// Builds a new [`PrivateKey`] from raw key material.
> +    #[must_use]
> +    pub fn from_raw(data: ed25519_dalek::SecretKey) -> Self {
> +        // [`SigningKey`] takes care of correct key clamping.
> +        Self(SigningKey::from(&data).to_bytes())
> +    }
> +}
> +
> +/// A single WireGuard peer.
> +#[derive(Serialize, Debug)]
> +#[serde(rename_all = "PascalCase")]
> +pub struct WireGuardPeer {
> +    /// Public key, matching the private key of of the remote peer.
> +    pub public_key: PublicKey,
> +    /// Additional key preshared between two peers. Adds an additional layer of symmetric-key
> +    /// cryptography to be mixed into the already existing public-key cryptography, for
> +    /// post-quantum resistance.
> +    pub preshared_key: PresharedKey,
> +    /// List of IPv4/v6 CIDRs from which incoming traffic for this peer is allowed and to which
> +    /// outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be specified for
> +    /// matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses.
> +    #[serde(rename = "AllowedIPs")]
> +    pub allowed_ips: Vec<Cidr>,

potentially missing skip_serializing_if = "Vec::is_empty"?

[snip]

> +#[cfg(test)]
> +mod tests {
> +    use std::net::Ipv4Addr;
> +
> +    use proxmox_network_types::ip_address::Cidr;
> +
> +    use crate::{PresharedKey, PrivateKey, WireGuardConfig, WireGuardInterface, WireGuardPeer};
> +
> +    fn mock_private_key(v: u8) -> PrivateKey {
> +        let base = v * 32;
> +        PrivateKey((base..base + 32).collect::<Vec<u8>>().try_into().unwrap())
> +    }
> +
> +    fn mock_preshared_key(v: u8) -> PresharedKey {
> +        let base = v * 32;
> +        PresharedKey((base..base + 32).collect::<Vec<u8>>().try_into().unwrap())
> +    }
> +
> +    #[test]
> +    fn single_peer() {
> +        let config = WireGuardConfig {
> +            interface: WireGuardInterface {
> +                private_key: mock_private_key(0),
> +                listen_port: Some(51820),
> +                fw_mark: Some(127),
> +            },
> +            peers: vec![WireGuardPeer {
> +                public_key: mock_private_key(1).public_key(),
> +                preshared_key: mock_preshared_key(1),
> +                allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 0, 0), 24).unwrap()],
> +                endpoint: Some("foo.example.com:51820".parse().unwrap()),
> +                persistent_keepalive: Some(25),
> +            }],
> +        };
> +
> +        pretty_assertions::assert_eq!(
> +            config.to_raw_config().unwrap(),
> +            "[Interface]
> +PrivateKey = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
> +ListenPort = 51820
> +FwMark = 127
> +
> +[Peer]
> +PublicKey = Kay64UG8yvCyLhqU000LxzYeUm0L/hLIl5S8kyKWbdc=
> +PresharedKey = ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=
> +AllowedIPs = 192.168.0.0/24
> +Endpoint = foo.example.com:51820
> +PersistentKeepalive = 25
> +"
> +        );
> +    }
> +
> +    #[test]
> +    fn multiple_peers() {
> +        let config = WireGuardConfig {
> +            interface: WireGuardInterface {
> +                private_key: mock_private_key(0),
> +                listen_port: Some(51820),
> +                fw_mark: None,
> +            },
> +            peers: vec![
> +                WireGuardPeer {
> +                    public_key: mock_private_key(1).public_key(),
> +                    preshared_key: mock_preshared_key(1),
> +                    allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 0, 0), 24).unwrap()],
> +                    endpoint: Some("foo.example.com:51820".parse().unwrap()),
> +                    persistent_keepalive: None,
> +                },
> +                WireGuardPeer {
> +                    public_key: mock_private_key(2).public_key(),
> +                    preshared_key: mock_preshared_key(2),
> +                    allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 1, 0), 24).unwrap()],
> +                    endpoint: None,
> +                    persistent_keepalive: Some(25),
> +                },
> +                WireGuardPeer {
> +                    public_key: mock_private_key(3).public_key(),
> +                    preshared_key: mock_preshared_key(3),
> +                    allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 2, 0), 24).unwrap()],
> +                    endpoint: None,
> +                    persistent_keepalive: None,
> +                },
> +            ],
> +        };
> +
> +        pretty_assertions::assert_eq!(
> +            config.to_raw_config().unwrap(),
> +            "[Interface]
> +PrivateKey = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
> +ListenPort = 51820
> +
> +[Peer]
> +PublicKey = Kay64UG8yvCyLhqU000LxzYeUm0L/hLIl5S8kyKWbdc=
> +PresharedKey = ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=
> +AllowedIPs = 192.168.0.0/24
> +Endpoint = foo.example.com:51820
> +
> +[Peer]
> +PublicKey = JUO5L/EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0=
> +PresharedKey = QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl8=
> +AllowedIPs = 192.168.1.0/24
> +PersistentKeepalive = 25
> +
> +[Peer]
> +PublicKey = F0VTtFbd38aQjsqxwQH+arIeK6oGF3lbfUOmNIKZP9U=
> +PresharedKey = YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
> +AllowedIPs = 192.168.2.0/24
> +"
> +        );
> +    }
> +}

maybe add some test cases for missing properties as well (or adapt the
existing ones)?




  reply	other threads:[~2026-02-13 10:12 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-16 15:33 [pve-devel] [PATCH proxmox{, -ve-rs} 00/11] sdn: add wireguard fabric configuration support Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 01/11] serde: implement ini serializer Christoph Heiss
2026-02-11 16:36   ` Stefan Hanreich
2026-02-11 18:40     ` Christoph Heiss
2026-02-12 10:59       ` Stefan Hanreich
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 02/11] serde: add base64 module for byte arrays Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 03/11] network-types: add ServiceEndpoint type as host/port tuple abstraction Christoph Heiss
2026-02-13 10:11   ` Stefan Hanreich
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 04/11] schema: provide integer schema for node ports Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 05/11] schema: api-types: add ed25519 base64 encoded key schema Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 06/11] wireguard: init configuration support crate Christoph Heiss
2026-02-13 10:13   ` Stefan Hanreich [this message]
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 07/11] wireguard: implement api for PublicKey Christoph Heiss
2026-02-13 10:12   ` Stefan Hanreich
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 08/11] wireguard: make per-peer preshared key optional Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 09/11] sdn-types: add wireguard-specific PersistentKeepalive api type Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 10/11] ve-config: fabric: refactor fabric config entry impl using macro Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 11/11] ve-config: sdn: fabrics: add wireguard section config types Christoph Heiss
2026-02-13 10:14 ` [pve-devel] [PATCH proxmox{, -ve-rs} 00/11] sdn: add wireguard fabric configuration support Stefan Hanreich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0c44a626-af78-4bb9-89a3-e5e5c6f6fd1b@proxmox.com \
    --to=s.hanreich@proxmox.com \
    --cc=c.heiss@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal