Fixes from the audit: input checks, apply order, sign-in limits

- The server endpoint must be a plain host name or IP address. It is
  written into client configs as is, so a newline could add lines such
  as PreUp, which wg-quick runs as root on the client.
- Listen addresses and the session length (1–720 hours) are checked.
  Before web settings or a restore are saved, the server tries the new
  listen addresses and certificate files, so a value it cannot start
  with is refused instead of stopping the service at the next restart.
- Kernel applies run one at a time and read the config once it is
  their turn, so an older config can no longer be applied last.
- Pending passkey sign-ins are capped: 10 per address, 1000 in total.
- Behind a local proxy, the last X-Forwarded-For entry is the client;
  earlier ones come from the client and are ignored.
- With LAN access off, peers are also kept from the IPv6 networks on
  the uplink, not only from its private IPv4 networks.
- A change that leaves no user with a password is refused, and so is a
  backup without one or from a newer version.
This commit is contained in:
Daniel Redetzke
2026-10-05 23:07:30 +03:00
parent aa4ca20296
commit 3e8dba6072
9 changed files with 485 additions and 28 deletions
+20 -19
View File
@@ -217,7 +217,8 @@ func (k *linuxKernel) Apply(c *Config) error {
if c.Server.IPv6Enabled {
_ = os.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte("1"), 0o644)
}
return applyFirewall(c, k.Uplink(c, false), k.Uplink(c, true), lanNetworks(k.Uplink(c, false)))
up4, up6 := k.Uplink(c, false), k.Uplink(c, true)
return applyFirewall(c, up4, up6, lanNetworks(up4, up6))
}
func (k *linuxKernel) Sample(iface string) ([]PeerSample, error) {
@@ -264,27 +265,27 @@ func (k *linuxKernel) Uplink(c *Config, v6 bool) string {
return l.Attrs().Name
}
// lanNetworks returns the private IPv4 networks on the uplink, used to block
// peers from the server's LAN when LAN access is off.
func lanNetworks(uplink string) []netip.Prefix {
if uplink == "" {
return nil
}
l, err := netlink.LinkByName(uplink)
if err != nil {
return nil
}
addrs, _ := netlink.AddrList(l, netlink.FAMILY_V4)
var out []netip.Prefix
for _, a := range addrs {
if !a.IP.IsPrivate() {
// lanNetworks returns the LAN networks on the IPv4 and IPv6 uplinks (see
// lanBlock), used to block peers from the server's LAN when LAN access is off.
func lanNetworks(uplinks ...string) []netip.Prefix {
var nets []netip.Prefix
for i, uplink := range uplinks {
if uplink == "" || slices.Contains(uplinks[:i], uplink) {
continue
}
ones, _ := a.Mask.Size()
ip, _ := netip.AddrFromSlice(a.IP.To4())
out = append(out, netip.PrefixFrom(ip, ones).Masked())
l, err := netlink.LinkByName(uplink)
if err != nil {
continue
}
addrs, _ := netlink.AddrList(l, netlink.FAMILY_ALL)
for _, a := range addrs {
ones, _ := a.Mask.Size()
if ip, ok := netip.AddrFromSlice(a.IP); ok {
nets = append(nets, netip.PrefixFrom(ip.Unmap(), ones))
}
}
}
return out
return lanBlock(nets)
}
// publicAddr reports the uplink's address for the health check: the first