Skip to main content

wireguard_conf/models/
peer.rs

1use derive_builder::Builder;
2use either::Either;
3use ipnet::IpNet;
4
5use std::fmt;
6use std::net::{IpAddr, Ipv6Addr};
7use std::{convert::Infallible, net::Ipv4Addr};
8
9#[cfg(feature = "serde")]
10#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
11use serde::{Deserialize, Serialize};
12
13use crate::prelude::*;
14
15/// Options for [`Peer::to_interface()`].
16#[derive(Clone, Copy, Debug, PartialEq, Default)]
17#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
18pub struct ToInterfaceOptions {
19    /// Option, for setting server as default gateway.
20    default_gateway: bool,
21
22    /// Option, for setting persistent keepalive to client's peer.
23    persistent_keepalive: u16,
24
25    /// Option for removing server-only AmneziaWG fields from the generated interface.
26    #[cfg(feature = "amneziawg")]
27    strip_server_data: bool,
28}
29
30impl ToInterfaceOptions {
31    /// Create new [`ToInterfaceOptions`].
32    #[must_use]
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Sets server as default gateway.
38    ///
39    /// When client interface will be generated, it will set `client_interface.peers[0].allowed_ips` to `0.0.0.0/0`
40    #[must_use]
41    pub fn default_gateway(mut self, value: bool) -> Self {
42        self.default_gateway = value;
43        self
44    }
45
46    /// Sets persistent keepalive to client's peer.
47    ///
48    #[must_use]
49    pub fn persistent_keepalive(mut self, value: u16) -> Self {
50        self.persistent_keepalive = value;
51        self
52    }
53
54    /// Removes server-only AmneziaWG settings from the generated interface.
55    #[must_use]
56    #[cfg(feature = "amneziawg")]
57    pub fn strip_server_data(mut self, value: bool) -> Self {
58        self.strip_server_data = value;
59        self
60    }
61}
62
63/// Struct, that represents `[Peer]` section in configuration.
64///
65/// [Wireguard docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#peer)
66#[must_use]
67#[derive(Clone, Debug, PartialEq, Builder)]
68#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
69#[builder(build_fn(private, name = "fallible_build", error = "Infallible"))]
70pub struct Peer {
71    /// Peer's endpoint.
72    ///
73    /// [Wireguard Docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#endpoint)
74    #[builder(setter(into, strip_option), default)]
75    pub endpoint: Option<String>,
76
77    /// Peer's allowed IPs.
78    ///
79    /// - */32 and */128 ipnets will be generated as regular ips (f.e. 1.2.3.4/32 -> 1.2.3.4)
80    ///
81    /// [Wireguard Docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#allowedips)
82    #[builder(setter(into), default)]
83    pub allowed_ips: Vec<IpNet>,
84
85    /// Peer's persistent keepalive.
86    ///
87    /// Represents in seconds how often to send an authenticated empty packet to the peer, for the
88    /// purpose of keeping a stateful firewall or NAT mapping valid persistently.
89    ///
90    /// Setting this value to `0` omits it in config.
91    ///
92    /// [Wireguard docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#persistentkeepalive)
93    #[builder(default)]
94    pub persistent_keepalive: u16,
95
96    /// Peer's key.
97    ///
98    /// If [`PrivateKey`] is provided, then peer can be exported to interface & full config.
99    /// Otherwise only to peer section of config.
100    #[builder(default = Either::Left(PrivateKey::random()))]
101    pub key: Either<PrivateKey, PublicKey>,
102
103    /// Peer's preshared-key.
104    #[builder(setter(strip_option), default)]
105    pub preshared_key: Option<PresharedKey>,
106}
107
108impl Peer {
109    /// Create new `PeerBuilder`. Alias for `PeerBuilder::new()`.
110    ///
111    /// ```rust
112    /// # use wireguard_conf::prelude::*;
113    /// # use wireguard_conf::as_ipnet;
114    /// #
115    /// let interface = Peer::builder()
116    ///     .allowed_ips([as_ipnet!("0.0.0.0/0")])
117    ///     // <snip>
118    ///     .build();
119    /// ```
120    #[must_use]
121    pub fn builder() -> PeerBuilder {
122        PeerBuilder::default()
123    }
124}
125
126impl PeerBuilder {
127    /// Create new `PeerBuilder`.
128    ///
129    /// ```rust
130    /// # use wireguard_conf::prelude::*;
131    /// # use wireguard_conf::as_ipnet;
132    /// #
133    /// let interface = PeerBuilder::new()
134    ///     .allowed_ips([as_ipnet!("0.0.0.0/0")])
135    ///     // <snip>
136    ///     .build();
137    /// ```
138    #[must_use]
139    pub fn new() -> Self {
140        Self::default()
141    }
142
143    /// Sets private key.
144    ///
145    /// Shorthand for `.key(Either::Left(value))`.
146    pub fn private_key(&mut self, value: PrivateKey) -> &mut Self {
147        self.key = Some(Either::Left(value));
148        self
149    }
150
151    /// Sets public key.
152    ///
153    /// Shorthand for `.key(Either::Right(value))`.
154    pub fn public_key(&mut self, value: PublicKey) -> &mut Self {
155        self.key = Some(Either::Right(value));
156        self
157    }
158
159    /// Builds an `Interface`.
160    pub fn build(&self) -> Peer {
161        self.fallible_build().unwrap_or_else(|_| unreachable!())
162    }
163}
164
165impl Peer {
166    /// Generate [`Interface`] from client's [`Peer`] and server's [`Interface`].
167    ///
168    /// `options`
169    ///
170    /// # Errors
171    ///
172    /// - [`WireguardError::NoPrivateKeyProvided`] -- peer don't have private key.
173    ///   You need to provide [`PrivateKey`] for creating interfaces from peers.
174    /// - [`WireguardError::NoAssignedIP`] -- no assigned ip found.
175    ///   This means that your peer doesn't have allowed ip, that is in interface's addresses
176    ///   network.
177    pub fn to_interface(
178        &self,
179        server_interface: &Interface,
180        options: ToInterfaceOptions,
181    ) -> WireguardResult<Interface> {
182        let Either::Left(private_key) = self.key.clone() else {
183            return Err(WireguardError::NoPrivateKeyProvided);
184        };
185
186        let assigned_ips: Vec<IpNet> = self
187            .allowed_ips
188            .iter()
189            .filter_map(|allowed_ip| {
190                for server_address in &server_interface.address {
191                    if server_address.contains(allowed_ip) {
192                        return IpNet::new(allowed_ip.addr(), server_address.prefix_len()).ok();
193                    }
194                }
195
196                None
197            })
198            .collect();
199
200        if assigned_ips.is_empty() {
201            return Err(WireguardError::NoAssignedIP);
202        }
203
204        let mut client_interface = Interface {
205            endpoint: None,
206
207            address: assigned_ips.clone(),
208            listen_port: None,
209            private_key,
210            dns: server_interface.dns.clone(),
211
212            table: None,
213            mtu: None,
214
215            #[cfg(feature = "amneziawg")]
216            amnezia_settings: server_interface.amnezia_settings.clone(),
217
218            pre_up: vec![],
219            pre_down: vec![],
220            post_up: vec![],
221            post_down: vec![],
222
223            peers: vec![server_interface.to_peer()],
224        };
225
226        if options.default_gateway {
227            client_interface.peers[0].allowed_ips = {
228                let mut allowed_ips = Vec::with_capacity(1);
229
230                if assigned_ips.iter().any(|ip| ip.addr().is_ipv4()) {
231                    allowed_ips.push(IpNet::new_assert(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0));
232                }
233
234                if assigned_ips.iter().any(|ip| ip.addr().is_ipv6()) {
235                    allowed_ips.push(IpNet::new_assert(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0));
236                }
237
238                allowed_ips
239            };
240        }
241
242        if options.persistent_keepalive != 0 {
243            client_interface.peers[0].persistent_keepalive = options.persistent_keepalive;
244        }
245
246        #[cfg(feature = "amneziawg")]
247        if options.strip_server_data
248            && let Some(settings) = &mut client_interface.amnezia_settings
249        {
250            settings.strip_server_data();
251        }
252
253        Ok(client_interface)
254    }
255}
256
257/// Implements [`fmt::Display`] for exporting peer.
258///
259/// # Note
260///
261/// It exports only `[Peer] ...` part. To export full interface, use [`Peer::to_interface()`]
262/// and then `.to_string()`
263impl fmt::Display for Peer {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        writeln!(f, "[Peer]")?;
266        if let Some(endpoint) = self.endpoint.clone() {
267            writeln!(f, "Endpoint = {endpoint}")?;
268        }
269        writeln!(
270            f,
271            "AllowedIPs = {}",
272            self.allowed_ips
273                .iter()
274                .map(std::string::ToString::to_string)
275                .collect::<Vec<String>>()
276                .join(",")
277        )?;
278        writeln!(
279            f,
280            "PublicKey = {}",
281            self.key.clone().right_or_else(|key| PublicKey::from(&key))
282        )?;
283        if let Some(preshared_key) = &self.preshared_key {
284            writeln!(f, "PresharedKey = {preshared_key}")?;
285        }
286        if self.persistent_keepalive != 0 {
287            writeln!(f, "PersistentKeepalive = {}", self.persistent_keepalive)?;
288        }
289
290        Ok(())
291    }
292}