mirror of
https://github.com/navidrome/navidrome.git
synced 2026-11-07 18:58:38 +01:00
38 lines
1.2 KiB
Go
38 lines
1.2 KiB
Go
|
|
// Package netguard keeps outbound connections driven by untrusted input away from internal addresses.
|
||
|
|
package netguard
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"net"
|
||
|
|
"net/netip"
|
||
|
|
"syscall"
|
||
|
|
)
|
||
|
|
|
||
|
|
var ErrPrivateAddress = errors.New("dial to private/loopback address blocked")
|
||
|
|
|
||
|
|
// IsPrivateIP reports whether ip is loopback, private, link-local or unspecified.
|
||
|
|
func IsPrivateIP(ip net.IP) bool {
|
||
|
|
return ip.IsLoopback() || ip.IsUnspecified() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast()
|
||
|
|
}
|
||
|
|
|
||
|
|
// DialControl returns a net.Dialer Control hook that rejects private addresses not covered by allowed.
|
||
|
|
// It sees the resolved IP, so DNS names, redirects and rebinding cannot get around it.
|
||
|
|
func DialControl(allowed ...netip.Prefix) func(network, address string, c syscall.RawConn) error {
|
||
|
|
return func(_, address string, _ syscall.RawConn) error {
|
||
|
|
ap, err := netip.ParseAddrPort(address)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("%w: unparseable address %q: %w", ErrPrivateAddress, address, err)
|
||
|
|
}
|
||
|
|
addr := ap.Addr().Unmap()
|
||
|
|
if !IsPrivateIP(addr.AsSlice()) {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
for _, p := range allowed {
|
||
|
|
if p.Contains(addr) {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return fmt.Errorf("%w: %s", ErrPrivateAddress, address)
|
||
|
|
}
|
||
|
|
}
|