Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+134
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go4.org/netipx"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/util/clientmetric"
|
||||
)
|
||||
|
||||
// v4BroadcastAddr is 255.255.255.255, the limited broadcast address.
|
||||
var v4BroadcastAddr = netip.AddrFrom4([4]byte{255, 255, 255, 255})
|
||||
|
||||
// viaAdditionsEnv is the name of an environment variable that can be set to
|
||||
// override the 4via6 host-scoped address restrictions. Only use this if you are
|
||||
// certain that there is not a better solution (e.g. perhaps tailscale serve,
|
||||
// possibly using a Service). Use of this feature will have security
|
||||
// implications: ensure that your ACLs/Grants are written appropriately.
|
||||
// The value of the environment variable is a comma separated list of IPv4
|
||||
// ranges (two IPv4 addresses separated by a hyphen), IPv4 prefixes (CIDR
|
||||
// notation), or IPv4 addresses. Spaces are permitted around the commas.
|
||||
const viaAdditionsEnv = "TS_4VIA6_ALLOW_LOCAL"
|
||||
|
||||
var viaTargetAdditions = sync.OnceValue(func() *netipx.IPSet {
|
||||
v, err := generateViaTargetAdditions(envknob.String(viaAdditionsEnv))
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
has4via6AdditionsMetric := clientmetric.NewGauge("4via6_allow_local")
|
||||
has4via6AdditionsMetric.Set(1)
|
||||
}
|
||||
|
||||
return v
|
||||
})
|
||||
|
||||
func generateViaTargetAdditions(rawAdditions string) (*netipx.IPSet, error) {
|
||||
rawAdditions = strings.TrimSpace(rawAdditions)
|
||||
if rawAdditions == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ips netipx.IPSetBuilder
|
||||
|
||||
rawAddrs := strings.Split(rawAdditions, ",")
|
||||
errs := make([]error, 0, len(rawAddrs))
|
||||
for _, rawAddr := range rawAddrs {
|
||||
rawAddr = strings.TrimSpace(rawAddr)
|
||||
if rawAddr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try parsing as a range
|
||||
if ipRange, err := netipx.ParseIPRange(rawAddr); err == nil {
|
||||
if !ipRange.From().Is4() || !ipRange.To().Is4() {
|
||||
errs = append(errs, fmt.Errorf("rejecting %v range %v: not IPv4", viaAdditionsEnv, ipRange))
|
||||
continue
|
||||
}
|
||||
|
||||
ips.AddRange(ipRange)
|
||||
continue
|
||||
}
|
||||
|
||||
// Try parsing as a prefix
|
||||
if prefix, err := netip.ParsePrefix(rawAddr); err == nil {
|
||||
if !prefix.Addr().Is4() {
|
||||
errs = append(errs, fmt.Errorf("rejecting %v prefix %v: not IPv4", viaAdditionsEnv, prefix))
|
||||
continue
|
||||
}
|
||||
|
||||
ips.AddPrefix(prefix)
|
||||
continue
|
||||
}
|
||||
|
||||
// Try parsing as an individual IP
|
||||
if addr, err := netip.ParseAddr(rawAddr); err == nil {
|
||||
if !addr.Is4() {
|
||||
errs = append(errs, fmt.Errorf("rejecting %v address %v: not IPv4", viaAdditionsEnv, addr))
|
||||
continue
|
||||
}
|
||||
|
||||
ips.Add(addr)
|
||||
continue
|
||||
}
|
||||
|
||||
errs = append(errs, fmt.Errorf("rejecting %v value %q: not a range, prefix, or address", viaAdditionsEnv, rawAddr))
|
||||
}
|
||||
|
||||
ipSet, err := ips.IPSet()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return ipSet, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// viaTargetAllowed reports whether ip may be forwarded to after unmapping a
|
||||
// 4via6 destination. The packet filter only sees the outer via address, so
|
||||
// this is the sole check on the embedded IPv4 target.
|
||||
func viaTargetAllowed(ip netip.Addr) bool {
|
||||
if !ip.Is4() {
|
||||
return false // UnmapVia only returns IPv4
|
||||
}
|
||||
if additions := viaTargetAdditions(); additions != nil {
|
||||
// Permit unusual configurations to use host-scoped IP addresses as
|
||||
// 4via6 destinations when provided in an envknob.
|
||||
if additions.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if ip.IsLoopback() || ip.IsMulticast() || ip.IsUnspecified() || ip == v4BroadcastAddr {
|
||||
return false
|
||||
}
|
||||
if tsaddr.IsTailscaleIP(ip) {
|
||||
// A CGNAT-range target may route to tailscale0 or to a site LAN
|
||||
// depending on the tailnet and OS (see shouldUseOneCGNATRoute);
|
||||
// it's hard to detect when forwarding would be OK, so deny always
|
||||
return false
|
||||
}
|
||||
if ip.IsLinkLocalUnicast() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in new issue
Block a user