Update dependencies

This commit is contained in:
bluepython508
2025-04-09 01:00:12 +01:00
parent f0641ffd6e
commit 5a9cfc022c
882 changed files with 68930 additions and 24201 deletions

View File

@@ -102,3 +102,18 @@ type C2NTLSCertInfo struct {
// TODO(bradfitz): add fields for whether an ACME fetch is currently in
// process and when it started, etc.
}
// C2NVIPServicesResponse is the response (from node to control) from the
// /vip-services handler.
//
// It returns the list of VIPServices that the node is currently serving with
// their port info and whether they are active or not. It also returns a hash of
// the response to allow the control server to detect changes.
type C2NVIPServicesResponse struct {
// VIPServices is the list of VIP services that the node is currently serving.
VIPServices []*VIPService `json:",omitempty"`
// ServicesHash is the hash of VIPServices to allow the control server to detect
// changes. This value matches what is reported in latest [Hostinfo.ServicesHash].
ServicesHash string
}

View File

@@ -96,12 +96,32 @@ type DERPRegion struct {
Latitude float64 `json:",omitempty"`
Longitude float64 `json:",omitempty"`
// Avoid is whether the client should avoid picking this as its home
// region. The region should only be used if a peer is there.
// Clients already using this region as their home should migrate
// away to a new region without Avoid set.
// Avoid is whether the client should avoid picking this as its home region.
// The region should only be used if a peer is there. Clients already using
// this region as their home should migrate away to a new region without
// Avoid set.
//
// Deprecated: because of bugs in past implementations combined with unclear
// docs that caused people to think the bugs were intentional, this field is
// deprecated. It was never supposed to cause STUN/DERP measurement probes,
// but due to bugs, it sometimes did. And then some parts of the code began
// to rely on that property. But then we were unable to use this field for
// its original purpose, nor its later imagined purpose, because various
// parts of the codebase thought it meant one thing and others thought it
// meant another. But it did something in the middle instead. So we're retiring
// it. Use NoMeasureNoHome instead.
Avoid bool `json:",omitempty"`
// NoMeasureNoHome says that this regions should not be measured for its
// latency distance (STUN, HTTPS, etc) or availability (e.g. captive portal
// checks) and should never be selected as the node's home region. However,
// if a peer declares this region as its home, then this client is allowed
// to connect to it for the purpose of communicating with that peer.
//
// This is what the now deprecated Avoid bool was supposed to mean
// originally but had implementation bugs and documentation omissions.
NoMeasureNoHome bool `json:",omitempty"`
// Nodes are the DERP nodes running in this region, in
// priority order for the current client. Client TLS
// connections should ideally only go to the first entry
@@ -139,6 +159,12 @@ type DERPNode struct {
// name. If empty, HostName is used. If CertName is non-empty,
// HostName is only used for the TCP dial (if IPv4/IPv6 are
// not present) + TLS ClientHello.
//
// As a special case, if CertName starts with "sha256-raw:",
// then the rest of the string is a hex-encoded SHA256 of the
// cert to expect. This is used for self-signed certs.
// In this case, the HostName field will typically be an IP
// address literal.
CertName string `json:",omitempty"`
// IPv4 optionally forces an IPv4 address to use, instead of using DNS.

View File

@@ -25,8 +25,10 @@ import (
"tailscale.com/types/opt"
"tailscale.com/types/structs"
"tailscale.com/types/tkatype"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
"tailscale.com/util/slicesx"
"tailscale.com/util/vizerror"
)
// CapabilityVersion represents the client's capability level. That
@@ -142,44 +144,88 @@ type CapabilityVersion int
// - 97: 2024-06-06: Client understands NodeAttrDisableSplitDNSWhenNoCustomResolvers
// - 98: 2024-06-13: iOS/tvOS clients may provide serial number as part of posture information
// - 99: 2024-06-14: Client understands NodeAttrDisableLocalDNSOverrideViaNRPT
// - 100: 2024-06-18: Client supports filtertype.Match.SrcCaps (issue #12542)
// - 100: 2024-06-18: Initial support for filtertype.Match.SrcCaps - actually usable in capver 109 (issue #12542)
// - 101: 2024-07-01: Client supports SSH agent forwarding when handling connections with /bin/su
// - 102: 2024-07-12: NodeAttrDisableMagicSockCryptoRouting support
// - 103: 2024-07-24: Client supports NodeAttrDisableCaptivePortalDetection
// - 104: 2024-08-03: SelfNodeV6MasqAddrForThisPeer now works
// - 105: 2024-08-05: Fixed SSH behavior on systems that use busybox (issue #12849)
// - 106: 2024-09-03: fix panic regression from cryptokey routing change (65fe0ba7b5)
const CurrentCapabilityVersion CapabilityVersion = 106
type StableID string
// - 107: 2024-10-30: add App Connector to conffile (PR #13942)
// - 108: 2024-11-08: Client sends ServicesHash in Hostinfo, understands c2n GET /vip-services.
// - 109: 2024-11-18: Client supports filtertype.Match.SrcCaps (issue #12542)
// - 110: 2024-12-12: removed never-before-used Tailscale SSH public key support (#14373)
// - 111: 2025-01-14: Client supports a peer having Node.HomeDERP (issue #14636)
// - 112: 2025-01-14: Client interprets AllowedIPs of nil as meaning same as Addresses
// - 113: 2025-01-20: Client communicates to control whether funnel is enabled by sending Hostinfo.IngressEnabled (#14688)
// - 114: 2025-01-30: NodeAttrMaxKeyDuration CapMap defined, clients might use it (no tailscaled code change) (#14829)
// - 115: 2025-03-07: Client understands DERPRegion.NoMeasureNoHome.
const CurrentCapabilityVersion CapabilityVersion = 115
// ID is an integer ID for a user, node, or login allocated by the
// control plane.
//
// To be nice, control plane servers should not use int64s that are too large to
// fit in a JavaScript number (see JavaScript's Number.MAX_SAFE_INTEGER).
// The Tailscale-hosted control plane stopped allocating large integers in
// March 2023 but nodes prior to that may have IDs larger than
// MAX_SAFE_INTEGER (2^53 1).
//
// IDs must not be zero or negative.
type ID int64
// UserID is an [ID] for a [User].
type UserID ID
func (u UserID) IsZero() bool {
return u == 0
}
// LoginID is an [ID] for a [Login].
//
// It is not used in the Tailscale client, but is used in the control plane.
type LoginID ID
func (u LoginID) IsZero() bool {
return u == 0
}
// NodeID is a unique integer ID for a node.
//
// It's global within a control plane URL ("tailscale up --login-server") and is
// (as of 2025-01-06) never re-used even after a node is deleted.
//
// To be nice, control plane servers should not use int64s that are too large to
// fit in a JavaScript number (see JavaScript's Number.MAX_SAFE_INTEGER).
// The Tailscale-hosted control plane stopped allocating large integers in
// March 2023 but nodes prior to that may have node IDs larger than
// MAX_SAFE_INTEGER (2^53 1).
//
// NodeIDs are not stable across control plane URLs. For more stable URLs,
// see [StableNodeID].
type NodeID ID
func (u NodeID) IsZero() bool {
return u == 0
}
type StableNodeID StableID
// StableNodeID is a string form of [NodeID].
//
// Different control plane servers should ideally have different StableNodeID
// suffixes for different sites or regions.
//
// Being a string, it's safer to use in JavaScript without worrying about the
// size of the integer, as documented on [NodeID].
//
// But in general, Tailscale APIs can accept either a [NodeID] integer or a
// [StableNodeID] string when referring to a node.
type StableNodeID string
func (u StableNodeID) IsZero() bool {
return u == ""
}
// User is an IPN user.
// User is a Tailscale user.
//
// A user can have multiple logins associated with it (e.g. gmail and github oauth).
// (Note: none of our UIs support this yet.)
@@ -192,34 +238,30 @@ func (u StableNodeID) IsZero() bool {
// have a general gmail address login associated with the user.
type User struct {
ID UserID
LoginName string `json:"-"` // not stored, filled from Login // TODO REMOVE
DisplayName string // if non-empty overrides Login field
ProfilePicURL string // if non-empty overrides Login field
Logins []LoginID
Created time.Time
}
// Login is a user from a specific identity provider, not associated with any
// particular tailnet.
type Login struct {
_ structs.Incomparable
ID LoginID
Provider string
LoginName string
DisplayName string
ProfilePicURL string
ID LoginID // unused in the Tailscale client
Provider string // "google", "github", "okta_foo", etc.
LoginName string // an email address or "email-ish" string (like alice@github)
DisplayName string // from the IdP
ProfilePicURL string // from the IdP
}
// A UserProfile is display-friendly data for a user.
// A UserProfile is display-friendly data for a [User].
// It includes the LoginName for display purposes but *not* the Provider.
// It also includes derived data from one of the user's logins.
type UserProfile struct {
ID UserID
LoginName string // "alice@smith.com"; for display purposes only (provider is not listed)
DisplayName string // "Alice Smith"
ProfilePicURL string
// Roles exists for legacy reasons, to keep old macOS clients
// happy. It JSON marshals as [].
Roles emptyStructJSONSlice
ProfilePicURL string `json:",omitempty"`
}
func (p *UserProfile) Equal(p2 *UserProfile) bool {
@@ -235,16 +277,6 @@ func (p *UserProfile) Equal(p2 *UserProfile) bool {
p.ProfilePicURL == p2.ProfilePicURL
}
type emptyStructJSONSlice struct{}
var emptyJSONSliceBytes = []byte("[]")
func (emptyStructJSONSlice) MarshalJSON() ([]byte, error) {
return emptyJSONSliceBytes, nil
}
func (emptyStructJSONSlice) UnmarshalJSON([]byte) error { return nil }
// RawMessage is a raw encoded JSON value. It implements Marshaler and
// Unmarshaler and can be used to delay JSON decoding or precompute a JSON
// encoding.
@@ -279,6 +311,7 @@ func MarshalCapJSON[T any](capRule T) (RawMessage, error) {
return RawMessage(string(bs)), nil
}
// Node is a Tailscale device in a tailnet.
type Node struct {
ID NodeID
StableID StableNodeID
@@ -302,19 +335,37 @@ type Node struct {
KeySignature tkatype.MarshaledSignature `json:",omitempty"`
Machine key.MachinePublic
DiscoKey key.DiscoPublic
Addresses []netip.Prefix // IP addresses of this Node directly
AllowedIPs []netip.Prefix // range of IP addresses to route to this node
Endpoints []netip.AddrPort `json:",omitempty"` // IP+port (public via STUN, and local LANs)
// DERP is this node's home DERP region ID integer, but shoved into an
// Addresses are the IP addresses of this Node directly.
Addresses []netip.Prefix
// AllowedIPs are the IP ranges to route to this node.
//
// As of CapabilityVersion 112, this may be nil (null or undefined) on the wire
// to mean the same as Addresses. Internally, it is always filled in with
// its possibly-implicit value.
AllowedIPs []netip.Prefix
Endpoints []netip.AddrPort `json:",omitempty"` // IP+port (public via STUN, and local LANs)
// LegacyDERPString is this node's home LegacyDERPString region ID integer, but shoved into an
// IP:port string for legacy reasons. The IP address is always "127.3.3.40"
// (a loopback address (127) followed by the digits over the letters DERP on
// a QWERTY keyboard (3.3.40)). The "port number" is the home DERP region ID
// a QWERTY keyboard (3.3.40)). The "port number" is the home LegacyDERPString region ID
// integer.
//
// TODO(bradfitz): simplify this legacy mess; add a new HomeDERPRegionID int
// field behind a new capver bump.
DERP string `json:",omitempty"` // DERP-in-IP:port ("127.3.3.40:N") endpoint
// Deprecated: HomeDERP has replaced this, but old servers might still send
// this field. See tailscale/tailscale#14636. Do not use this field in code
// other than in the upgradeNode func, which canonicalizes it to HomeDERP
// if it arrives as a LegacyDERPString string on the wire.
LegacyDERPString string `json:"DERP,omitempty"` // DERP-in-IP:port ("127.3.3.40:N") endpoint
// HomeDERP is the modern version of the DERP string field, with just an
// integer. The client advertises support for this as of capver 111.
//
// HomeDERP may be zero if not (yet) known, but ideally always be non-zero
// for magicsock connectivity to function normally.
HomeDERP int `json:",omitempty"` // DERP region ID of the node's home DERP
Hostinfo HostinfoView
Created time.Time
@@ -559,6 +610,11 @@ func (n *Node) InitDisplayNames(networkMagicDNSSuffix string) {
n.ComputedNameWithHost = nameWithHost
}
// MachineStatus is the state of a [Node]'s approval into a tailnet.
//
// A "node" and a "machine" are often 1:1, but technically a Tailscale
// daemon has one machine key and can have multiple nodes (e.g. different
// users on Windows) for that one machine key.
type MachineStatus int
const (
@@ -771,7 +827,7 @@ type Hostinfo struct {
// "5.10.0-17-amd64".
OSVersion string `json:",omitempty"`
Container opt.Bool `json:",omitempty"` // whether the client is running in a container
Container opt.Bool `json:",omitempty"` // best-effort whether the client is running in a container
Env string `json:",omitempty"` // a hostinfo.EnvType in string form
Distro string `json:",omitempty"` // "debian", "ubuntu", "nixos", ...
DistroVersion string `json:",omitempty"` // "20.04", ...
@@ -780,15 +836,23 @@ type Hostinfo struct {
// App is used to disambiguate Tailscale clients that run using tsnet.
App string `json:",omitempty"` // "k8s-operator", "golinks", ...
Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux
Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3")
PushDeviceToken string `json:",omitempty"` // macOS/iOS APNs device token for notifications (and Android in the future)
Hostname string `json:",omitempty"` // name of the host the client runs on
ShieldsUp bool `json:",omitempty"` // indicates whether the host is blocking incoming connections
ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user
NoLogsNoSupport bool `json:",omitempty"` // indicates that the user has opted out of sending logs and support
WireIngress bool `json:",omitempty"` // indicates that the node wants the option to receive ingress connections
Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux
Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3")
PushDeviceToken string `json:",omitempty"` // macOS/iOS APNs device token for notifications (and Android in the future)
Hostname string `json:",omitempty"` // name of the host the client runs on
ShieldsUp bool `json:",omitempty"` // indicates whether the host is blocking incoming connections
ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user
NoLogsNoSupport bool `json:",omitempty"` // indicates that the user has opted out of sending logs and support
// WireIngress indicates that the node would like to be wired up server-side
// (DNS, etc) to be able to use Tailscale Funnel, even if it's not currently
// enabled. For example, the user might only use it for intermittent
// foreground CLI serve sessions, for which they'd like it to work right
// away, even if it's disabled most of the time. As an optimization, this is
// only sent if IngressEnabled is false, as IngressEnabled implies that this
// option is true.
WireIngress bool `json:",omitempty"`
IngressEnabled bool `json:",omitempty"` // if the node has any funnel endpoint enabled
AllowsUpdate bool `json:",omitempty"` // indicates that the node has opted-in to admin-console-drive remote updates
Machine string `json:",omitempty"` // the current host's machine type (uname -m)
GoArch string `json:",omitempty"` // GOARCH value (of the built binary)
@@ -804,6 +868,7 @@ type Hostinfo struct {
Userspace opt.Bool `json:",omitempty"` // if the client is running in userspace (netstack) mode
UserspaceRouter opt.Bool `json:",omitempty"` // if the client's subnet router is running in userspace (netstack) mode
AppConnector opt.Bool `json:",omitempty"` // if the client is running the app-connector service
ServicesHash string `json:",omitempty"` // opaque hash of the most recent list of tailnet services, change in hash indicates config should be fetched via c2n
// Location represents geographical location data about a
// Tailscale host. Location is optional and only set if
@@ -814,6 +879,61 @@ type Hostinfo struct {
// require changes to Hostinfo.Equal.
}
// ServiceName is the name of a service, of the form `svc:dns-label`. Services
// represent some kind of application provided for users of the tailnet with a
// MagicDNS name and possibly dedicated IP addresses. Currently (2024-01-21),
// the only type of service is [VIPService].
// This is not related to the older [Service] used in [Hostinfo.Services].
type ServiceName string
// Validate validates if the service name is formatted correctly.
// We only allow valid DNS labels, since the expectation is that these will be
// used as parts of domain names. All errors are [vizerror.Error].
func (sn ServiceName) Validate() error {
bareName, ok := strings.CutPrefix(string(sn), "svc:")
if !ok {
return vizerror.Errorf("%q is not a valid service name: must start with 'svc:'", sn)
}
if bareName == "" {
return vizerror.Errorf("%q is not a valid service name: must not be empty after the 'svc:' prefix", sn)
}
return dnsname.ValidLabel(bareName)
}
// String implements [fmt.Stringer].
func (sn ServiceName) String() string {
return string(sn)
}
// WithoutPrefix is the name of the service without the `svc:` prefix, used for
// DNS names. If the name does not include the prefix (which means
// [ServiceName.Validate] would return an error) then it returns "".
func (sn ServiceName) WithoutPrefix() string {
bareName, ok := strings.CutPrefix(string(sn), "svc:")
if !ok {
return ""
}
return bareName
}
// VIPService represents a service created on a tailnet from the
// perspective of a node providing that service. These services
// have an virtual IP (VIP) address pair distinct from the node's IPs.
type VIPService struct {
// Name is the name of the service. The Name uniquely identifies a service
// on a particular tailnet, and so also corresponds uniquely to the pair of
// IP addresses belonging to the VIP service.
Name ServiceName
// Ports specify which ProtoPorts are made available by this node
// on the service's IPs.
Ports []ProtoPortRange
// Active specifies whether new requests for the service should be
// sent to this node by control.
Active bool
}
// TailscaleSSHEnabled reports whether or not this node is acting as a
// Tailscale SSH server.
func (hi *Hostinfo) TailscaleSSHEnabled() bool {
@@ -824,14 +944,6 @@ func (hi *Hostinfo) TailscaleSSHEnabled() bool {
func (v HostinfoView) TailscaleSSHEnabled() bool { return v.ж.TailscaleSSHEnabled() }
// TailscaleFunnelEnabled reports whether or not this node has explicitly
// enabled Funnel.
func (hi *Hostinfo) TailscaleFunnelEnabled() bool {
return hi != nil && hi.WireIngress
}
func (v HostinfoView) TailscaleFunnelEnabled() bool { return v.ж.TailscaleFunnelEnabled() }
// NetInfo contains information about the host's network state.
type NetInfo struct {
// MappingVariesByDestIP says whether the host's NAT mappings
@@ -975,68 +1087,6 @@ func (h *Hostinfo) Equal(h2 *Hostinfo) bool {
return reflect.DeepEqual(h, h2)
}
// HowUnequal returns a list of paths through Hostinfo where h and h2 differ.
// If they differ in nil-ness, the path is "nil", otherwise the path is like
// "ShieldsUp" or "NetInfo.nil" or "NetInfo.PCP".
func (h *Hostinfo) HowUnequal(h2 *Hostinfo) (path []string) {
return appendStructPtrDiff(nil, "", reflect.ValueOf(h), reflect.ValueOf(h2))
}
func appendStructPtrDiff(base []string, pfx string, p1, p2 reflect.Value) (ret []string) {
ret = base
if p1.IsNil() && p2.IsNil() {
return base
}
mkPath := func(b string) string {
if pfx == "" {
return b
}
return pfx + "." + b
}
if p1.IsNil() || p2.IsNil() {
return append(base, mkPath("nil"))
}
v1, v2 := p1.Elem(), p2.Elem()
t := v1.Type()
for i, n := 0, t.NumField(); i < n; i++ {
sf := t.Field(i)
switch sf.Type.Kind() {
case reflect.String:
if v1.Field(i).String() != v2.Field(i).String() {
ret = append(ret, mkPath(sf.Name))
}
continue
case reflect.Bool:
if v1.Field(i).Bool() != v2.Field(i).Bool() {
ret = append(ret, mkPath(sf.Name))
}
continue
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v1.Field(i).Int() != v2.Field(i).Int() {
ret = append(ret, mkPath(sf.Name))
}
continue
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if v1.Field(i).Uint() != v2.Field(i).Uint() {
ret = append(ret, mkPath(sf.Name))
}
continue
case reflect.Slice, reflect.Map:
if !reflect.DeepEqual(v1.Field(i).Interface(), v2.Field(i).Interface()) {
ret = append(ret, mkPath(sf.Name))
}
continue
case reflect.Ptr:
if sf.Type.Elem().Kind() == reflect.Struct {
ret = appendStructPtrDiff(ret, sf.Name, v1.Field(i), v2.Field(i))
continue
}
}
panic(fmt.Sprintf("unsupported type at %s: %s", mkPath(sf.Name), sf.Type.String()))
}
return ret
}
// SignatureType specifies a scheme for signing RegisterRequest messages. It
// specifies the crypto algorithms to use, the contents of what is signed, and
// any other relevant details. Historically, requests were unsigned so the zero
@@ -1117,11 +1167,11 @@ type RegisterResponseAuth struct {
AuthKey string `json:",omitempty"`
}
// RegisterRequest is sent by a client to register the key for a node.
// It is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box,
// using the local machine key, and sent to:
// RegisterRequest is a request to register a key for a node.
//
// https://login.tailscale.com/machine/<mkey hex>
// This is JSON-encoded and sent over the control plane connection to:
//
// POST https://<control-plane>/machine/register.
type RegisterRequest struct {
_ structs.Incomparable
@@ -1237,10 +1287,9 @@ type Endpoint struct {
// The request includes a copy of the client's current set of WireGuard
// endpoints and general host information.
//
// The request is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box,
// using the local machine key, and sent to:
// This is JSON-encoded and sent over the control plane connection to:
//
// https://login.tailscale.com/machine/<mkey hex>/map
// POST https://<control-plane>/machine/map
type MapRequest struct {
// Version is incremented whenever the client code changes enough that
// we want to signal to the control server that we're capable of something
@@ -1355,7 +1404,7 @@ var PortRangeAny = PortRange{0, 65535}
type NetPortRange struct {
_ structs.Incomparable
IP string // IP, CIDR, Range, or "*" (same formats as FilterRule.SrcIPs)
Bits *int // deprecated; the 2020 way to turn IP into a CIDR. See FilterRule.SrcBits.
Bits *int `json:",omitempty"` // deprecated; the 2020 way to turn IP into a CIDR. See FilterRule.SrcBits.
Ports PortRange
}
@@ -1417,7 +1466,7 @@ const (
// NodeCapMap is a map of capabilities to their optional values. It is valid for
// a capability to have no values (nil slice); such capabilities can be tested
// for by using the Contains method.
// for by using the [NodeCapMap.Contains] method.
//
// See [NodeCapability] for more information on keys.
type NodeCapMap map[NodeCapability][]RawMessage
@@ -1431,12 +1480,19 @@ func (c NodeCapMap) Equal(c2 NodeCapMap) bool {
// If cap does not exist in cm, it returns (nil, nil).
// It returns an error if the values cannot be unmarshaled into the provided type.
func UnmarshalNodeCapJSON[T any](cm NodeCapMap, cap NodeCapability) ([]T, error) {
vals, ok := cm[cap]
return UnmarshalNodeCapViewJSON[T](views.MapSliceOf(cm), cap)
}
// UnmarshalNodeCapViewJSON unmarshals each JSON value in cm.Get(cap) as T.
// If cap does not exist in cm, it returns (nil, nil).
// It returns an error if the values cannot be unmarshaled into the provided type.
func UnmarshalNodeCapViewJSON[T any](cm views.MapSlice[NodeCapability, RawMessage], cap NodeCapability) ([]T, error) {
vals, ok := cm.GetOk(cap)
if !ok {
return nil, nil
}
out := make([]T, 0, len(vals))
for _, v := range vals {
out := make([]T, 0, vals.Len())
for _, v := range vals.All() {
var t T
if err := json.Unmarshal([]byte(v), &t); err != nil {
return nil, err
@@ -1466,12 +1522,19 @@ type PeerCapMap map[PeerCapability][]RawMessage
// If cap does not exist in cm, it returns (nil, nil).
// It returns an error if the values cannot be unmarshaled into the provided type.
func UnmarshalCapJSON[T any](cm PeerCapMap, cap PeerCapability) ([]T, error) {
vals, ok := cm[cap]
return UnmarshalCapViewJSON[T](views.MapSliceOf(cm), cap)
}
// UnmarshalCapViewJSON unmarshals each JSON value in cm.Get(cap) as T.
// If cap does not exist in cm, it returns (nil, nil).
// It returns an error if the values cannot be unmarshaled into the provided type.
func UnmarshalCapViewJSON[T any](cm views.MapSlice[PeerCapability, RawMessage], cap PeerCapability) ([]T, error) {
vals, ok := cm.GetOk(cap)
if !ok {
return nil, nil
}
out := make([]T, 0, len(vals))
for _, v := range vals {
out := make([]T, 0, vals.Len())
for _, v := range vals.All() {
var t T
if err := json.Unmarshal([]byte(v), &t); err != nil {
return nil, err
@@ -1666,9 +1729,14 @@ const (
PingPeerAPI PingType = "peerapi"
)
// PingRequest with no IP and Types is a request to send an HTTP request to prove the
// long-polling client is still connected.
// PingRequest with Types and IP, will send a ping to the IP and send a POST
// PingRequest is a request from the control plane to the local node to probe
// something.
//
// A PingRequest with no IP and Types is a request from the control plane to the
// local node to send an HTTP request to a URL to prove the long-polling client
// is still connected.
//
// A PingRequest with Types and IP, will send a ping to the IP and send a POST
// request containing a PingResponse to the URL containing results.
type PingRequest struct {
// URL is the URL to reply to the PingRequest to.
@@ -1833,7 +1901,7 @@ type MapResponse struct {
// PeersChangedPatch, if non-nil, means that node(s) have changed.
// This is a lighter version of the older PeersChanged support that
// only supports certain types of updates
// only supports certain types of updates.
//
// These are applied after Peers* above, but in practice the
// control server should only send these on their own, without
@@ -1962,10 +2030,6 @@ type MapResponse struct {
// auto-update setting doesn't change if the tailnet admin flips the
// default after the node registered.
DefaultAutoUpdate opt.Bool `json:",omitempty"`
// MaxKeyDuration describes the MaxKeyDuration setting for the tailnet.
// If zero, the value is unchanged.
MaxKeyDuration time.Duration `json:",omitempty"`
}
// ClientVersion is information about the latest client version that's available
@@ -2081,7 +2145,8 @@ func (n *Node) Equal(n2 *Node) bool {
slicesx.EqualSameNil(n.AllowedIPs, n2.AllowedIPs) &&
slicesx.EqualSameNil(n.PrimaryRoutes, n2.PrimaryRoutes) &&
slicesx.EqualSameNil(n.Endpoints, n2.Endpoints) &&
n.DERP == n2.DERP &&
n.LegacyDERPString == n2.LegacyDERPString &&
n.HomeDERP == n2.HomeDERP &&
n.Cap == n2.Cap &&
n.Hostinfo.Equal(n2.Hostinfo) &&
n.Created.Equal(n2.Created) &&
@@ -2353,20 +2418,45 @@ const (
// automatically when the network state changes.
NodeAttrDisableCaptivePortalDetection NodeCapability = "disable-captive-portal-detection"
// NodeAttrDisableSkipStatusQueue is set when the node should disable skipping
// of queued netmap.NetworkMap between the controlclient and LocalBackend.
// See tailscale/tailscale#14768.
NodeAttrDisableSkipStatusQueue NodeCapability = "disable-skip-status-queue"
// NodeAttrSSHEnvironmentVariables enables logic for handling environment variables sent
// via SendEnv in the SSH server and applying them to the SSH session.
NodeAttrSSHEnvironmentVariables NodeCapability = "ssh-env-vars"
// NodeAttrServiceHost indicates the VIP Services for which the client is
// approved to act as a service host, and which IP addresses are assigned
// to those VIP Services. Any VIP Services that the client is not
// advertising can be ignored.
// Each value of this key in [NodeCapMap] is of type [ServiceIPMappings].
// If multiple values of this key exist, they should be merged in sequence
// (replace conflicting keys).
NodeAttrServiceHost NodeCapability = "service-host"
// NodeAttrMaxKeyDuration represents the MaxKeyDuration setting on the
// tailnet. The value of this key in [NodeCapMap] will be only one entry of
// type float64 representing the duration in seconds. This cap will be
// omitted if the tailnet's MaxKeyDuration is the default.
NodeAttrMaxKeyDuration NodeCapability = "tailnet.maxKeyDuration"
// NodeAttrNativeIPV4 contains the IPV4 address of the node in its
// native tailnet. This is currently only sent to Hello, in its
// peer node list.
NodeAttrNativeIPV4 NodeCapability = "native-ipv4"
)
// SetDNSRequest is a request to add a DNS record.
//
// This is used for ACME DNS-01 challenges (so people can use
// LetsEncrypt, etc).
// This is used to let tailscaled clients complete their ACME DNS-01 challenges
// (so people can use LetsEncrypt, etc) to get TLS certificates for
// their foo.bar.ts.net MagicDNS names.
//
// The request is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box,
// using the local machine key, and sent to:
// This is JSON-encoded and sent over the control plane connection to:
//
// https://login.tailscale.com/machine/<mkey hex>/set-dns
// POST https://<control-plane>/machine/set-dns
type SetDNSRequest struct {
// Version is the client's capabilities
// (CurrentCapabilityVersion) when using the Noise transport.
@@ -2396,7 +2486,9 @@ type SetDNSRequest struct {
type SetDNSResponse struct{}
// HealthChangeRequest is the JSON request body type used to report
// node health changes to https://<control>/machine/<mkey hex>/update-health.
// node health changes to:
//
// POST https://<control-plane>/machine/update-health.
type HealthChangeRequest struct {
Subsys string // a health.Subsystem value in string form
Error string // or empty if cleared
@@ -2406,6 +2498,38 @@ type HealthChangeRequest struct {
NodeKey key.NodePublic
}
// SetDeviceAttributesRequest is a request to update the
// current node's device posture attributes.
//
// As of 2024-12-30, this is an experimental dev feature
// for internal testing. See tailscale/corp#24690.
//
// This is JSON-encoded and sent over the control plane connection to:
//
// PATCH https://<control-plane>/machine/set-device-attr
type SetDeviceAttributesRequest struct {
// Version is the current binary's [CurrentCapabilityVersion].
Version CapabilityVersion
// NodeKey identifies the node to modify. It should be the currently active
// node and is an error if not.
NodeKey key.NodePublic
// Update is a map of device posture attributes to update.
// Attributes not in the map are left unchanged.
Update AttrUpdate
}
// AttrUpdate is a map of attributes to update.
// Attributes not in the map are left unchanged.
// The value can be a string, float64, bool, or nil to delete.
//
// See https://tailscale.com/s/api-device-posture-attrs.
//
// TODO(bradfitz): add struct type for specifying optional associated data
// for each attribute value, like an expiry time?
type AttrUpdate map[string]any
// SSHPolicy is the policy for how to handle incoming SSH connections
// over Tailscale.
type SSHPolicy struct {
@@ -2481,16 +2605,13 @@ type SSHPrincipal struct {
Any bool `json:"any,omitempty"` // if true, match any connection
// TODO(bradfitz): add StableUserID, once that exists
// PubKeys, if non-empty, means that this SSHPrincipal only
// matches if one of these public keys is presented by the user.
// UnusedPubKeys was public key support. It never became an official product
// feature and so as of 2024-12-12 is being removed.
// This stub exists to remind us not to re-use the JSON field name "pubKeys"
// in the future if we bring it back with different semantics.
//
// As a special case, if len(PubKeys) == 1 and PubKeys[0] starts
// with "https://", then it's fetched (like https://github.com/username.keys).
// In that case, the following variable expansions are also supported
// in the URL:
// * $LOGINNAME_EMAIL ("foo@bar.com" or "foo@github")
// * $LOGINNAME_LOCALPART (the "foo" from either of the above)
PubKeys []string `json:"pubKeys,omitempty"`
// Deprecated: do not use. It does nothing.
UnusedPubKeys []string `json:"pubKeys,omitempty"`
}
// SSHAction is how to handle an incoming connection.
@@ -2575,6 +2696,8 @@ type SSHRecorderFailureAction struct {
// SSHEventNotifyRequest is the JSON payload sent to the NotifyURL
// for an SSH event.
//
// POST https://<control-plane>/[...varies, sent in SSH policy...]
type SSHEventNotifyRequest struct {
// EventType is the type of notify request being sent.
EventType SSHEventType
@@ -2635,9 +2758,9 @@ type SSHRecordingAttempt struct {
FailureMessage string
}
// QueryFeatureRequest is a request sent to "/machine/feature/query"
// to get instructions on how to enable a feature, such as Funnel,
// for the node's tailnet.
// QueryFeatureRequest is a request sent to "POST /machine/feature/query" to get
// instructions on how to enable a feature, such as Funnel, for the node's
// tailnet.
//
// See QueryFeatureResponse for response structure.
type QueryFeatureRequest struct {
@@ -2726,7 +2849,7 @@ type OverTLSPublicKeyResponse struct {
// The token can be presented to any resource provider which offers OIDC
// Federation.
//
// It is JSON-encoded and sent over Noise to "/machine/id-token".
// It is JSON-encoded and sent over Noise to "POST /machine/id-token".
type TokenRequest struct {
// CapVersion is the client's current CapabilityVersion.
CapVersion CapabilityVersion
@@ -2841,3 +2964,51 @@ type EarlyNoise struct {
// For some request types, the header may have multiple values. (e.g. OldNodeKey
// vs NodeKey)
const LBHeader = "Ts-Lb"
// ServiceIPMappings maps ServiceName to lists of IP addresses. This is used
// as the value of the [NodeAttrServiceHost] capability, to inform service hosts
// what IP addresses they need to listen on for each service that they are
// advertising.
//
// This is of the form:
//
// {
// "svc:samba": ["100.65.32.1", "fd7a:115c:a1e0::1234"],
// "svc:web": ["100.102.42.3", "fd7a:115c:a1e0::abcd"],
// }
//
// where the IP addresses are the IPs of the VIP services. These IPs are also
// provided in AllowedIPs, but this lets the client know which services
// correspond to those IPs. Any services that don't correspond to a service
// this client is hosting can be ignored.
type ServiceIPMappings map[ServiceName][]netip.Addr
// ClientAuditAction represents an auditable action that a client can report to the
// control plane. These actions must correspond to the supported actions
// in the control plane.
type ClientAuditAction string
const (
// AuditNodeDisconnect action is sent when a node has disconnected
// from the control plane. The details must include a reason in the Details
// field, either generated, or entered by the user.
AuditNodeDisconnect = ClientAuditAction("DISCONNECT_NODE")
)
// AuditLogRequest represents an audit log request to be sent to the control plane.
//
// This is JSON-encoded and sent over the control plane connection to:
// POST https://<control-plane>/machine/audit-log
type AuditLogRequest struct {
// Version is the client's current CapabilityVersion.
Version CapabilityVersion `json:",omitempty"`
// NodeKey is the client's current node key.
NodeKey key.NodePublic `json:",omitzero"`
// Action is the action to be logged. It must correspond to a known action in the control plane.
Action ClientAuditAction `json:",omitempty"`
// Details is an opaque string, specific to the action being logged. Empty strings may not
// be valid depending on the action being logged.
Details string `json:",omitempty"`
// Timestamp is the time at which the audit log was generated on the node.
Timestamp time.Time `json:",omitzero"`
}

View File

@@ -26,17 +26,14 @@ func (src *User) Clone() *User {
}
dst := new(User)
*dst = *src
dst.Logins = append(src.Logins[:0:0], src.Logins...)
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _UserCloneNeedsRegeneration = User(struct {
ID UserID
LoginName string
DisplayName string
ProfilePicURL string
Logins []LoginID
Created time.Time
}{})
@@ -102,7 +99,8 @@ var _NodeCloneNeedsRegeneration = Node(struct {
Addresses []netip.Prefix
AllowedIPs []netip.Prefix
Endpoints []netip.AddrPort
DERP string
LegacyDERPString string
HomeDERP int
Hostinfo HostinfoView
Created time.Time
Cap CapabilityVersion
@@ -168,6 +166,7 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct {
ShareeNode bool
NoLogsNoSupport bool
WireIngress bool
IngressEnabled bool
AllowsUpdate bool
Machine string
GoArch string
@@ -183,6 +182,7 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct {
Userspace opt.Bool
UserspaceRouter opt.Bool
AppConnector opt.Bool
ServicesHash string
Location *Location
}{})
@@ -301,7 +301,6 @@ func (src *RegisterResponse) Clone() *RegisterResponse {
}
dst := new(RegisterResponse)
*dst = *src
dst.User = *src.User.Clone()
dst.NodeKeySignature = append(src.NodeKeySignature[:0:0], src.NodeKeySignature...)
return dst
}
@@ -417,13 +416,14 @@ func (src *DERPRegion) Clone() *DERPRegion {
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _DERPRegionCloneNeedsRegeneration = DERPRegion(struct {
RegionID int
RegionCode string
RegionName string
Latitude float64
Longitude float64
Avoid bool
Nodes []*DERPNode
RegionID int
RegionCode string
RegionName string
Latitude float64
Longitude float64
Avoid bool
NoMeasureNoHome bool
Nodes []*DERPNode
}{})
// Clone makes a deep copy of DERPMap.
@@ -555,17 +555,17 @@ func (src *SSHPrincipal) Clone() *SSHPrincipal {
}
dst := new(SSHPrincipal)
*dst = *src
dst.PubKeys = append(src.PubKeys[:0:0], src.PubKeys...)
dst.UnusedPubKeys = append(src.UnusedPubKeys[:0:0], src.UnusedPubKeys...)
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _SSHPrincipalCloneNeedsRegeneration = SSHPrincipal(struct {
Node StableNodeID
NodeIP string
UserLogin string
Any bool
PubKeys []string
Node StableNodeID
NodeIP string
UserLogin string
Any bool
UnusedPubKeys []string
}{})
// Clone makes a deep copy of ControlDialPlan.
@@ -624,7 +624,6 @@ var _UserProfileCloneNeedsRegeneration = UserProfile(struct {
LoginName string
DisplayName string
ProfilePicURL string
Roles emptyStructJSONSlice
}{})
// Clone duplicates src into dst and reports whether it succeeded.

View File

@@ -21,7 +21,7 @@ import (
//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile
// View returns a readonly view of User.
// View returns a read-only view of User.
func (p *User) View() UserView {
return UserView{ж: p}
}
@@ -37,7 +37,7 @@ type UserView struct {
ж *User
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v UserView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -66,24 +66,20 @@ func (v *UserView) UnmarshalJSON(b []byte) error {
return nil
}
func (v UserView) ID() UserID { return v.ж.ID }
func (v UserView) LoginName() string { return v.ж.LoginName }
func (v UserView) DisplayName() string { return v.ж.DisplayName }
func (v UserView) ProfilePicURL() string { return v.ж.ProfilePicURL }
func (v UserView) Logins() views.Slice[LoginID] { return views.SliceOf(v.ж.Logins) }
func (v UserView) Created() time.Time { return v.ж.Created }
func (v UserView) ID() UserID { return v.ж.ID }
func (v UserView) DisplayName() string { return v.ж.DisplayName }
func (v UserView) ProfilePicURL() string { return v.ж.ProfilePicURL }
func (v UserView) Created() time.Time { return v.ж.Created }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _UserViewNeedsRegeneration = User(struct {
ID UserID
LoginName string
DisplayName string
ProfilePicURL string
Logins []LoginID
Created time.Time
}{})
// View returns a readonly view of Node.
// View returns a read-only view of Node.
func (p *Node) View() NodeView {
return NodeView{ж: p}
}
@@ -99,7 +95,7 @@ type NodeView struct {
ж *Node
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v NodeView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -143,27 +139,18 @@ func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoK
func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) }
func (v NodeView) AllowedIPs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.AllowedIPs) }
func (v NodeView) Endpoints() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Endpoints) }
func (v NodeView) DERP() string { return v.ж.DERP }
func (v NodeView) LegacyDERPString() string { return v.ж.LegacyDERPString }
func (v NodeView) HomeDERP() int { return v.ж.HomeDERP }
func (v NodeView) Hostinfo() HostinfoView { return v.ж.Hostinfo }
func (v NodeView) Created() time.Time { return v.ж.Created }
func (v NodeView) Cap() CapabilityVersion { return v.ж.Cap }
func (v NodeView) Tags() views.Slice[string] { return views.SliceOf(v.ж.Tags) }
func (v NodeView) PrimaryRoutes() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.PrimaryRoutes) }
func (v NodeView) LastSeen() *time.Time {
if v.ж.LastSeen == nil {
return nil
}
x := *v.ж.LastSeen
return &x
func (v NodeView) LastSeen() views.ValuePointer[time.Time] {
return views.ValuePointerOf(v.ж.LastSeen)
}
func (v NodeView) Online() *bool {
if v.ж.Online == nil {
return nil
}
x := *v.ж.Online
return &x
}
func (v NodeView) Online() views.ValuePointer[bool] { return views.ValuePointerOf(v.ж.Online) }
func (v NodeView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
func (v NodeView) Capabilities() views.Slice[NodeCapability] { return views.SliceOf(v.ж.Capabilities) }
@@ -176,20 +163,12 @@ func (v NodeView) ComputedName() string { return v.ж.ComputedName }
func (v NodeView) ComputedNameWithHost() string { return v.ж.ComputedNameWithHost }
func (v NodeView) DataPlaneAuditLogID() string { return v.ж.DataPlaneAuditLogID }
func (v NodeView) Expired() bool { return v.ж.Expired }
func (v NodeView) SelfNodeV4MasqAddrForThisPeer() *netip.Addr {
if v.ж.SelfNodeV4MasqAddrForThisPeer == nil {
return nil
}
x := *v.ж.SelfNodeV4MasqAddrForThisPeer
return &x
func (v NodeView) SelfNodeV4MasqAddrForThisPeer() views.ValuePointer[netip.Addr] {
return views.ValuePointerOf(v.ж.SelfNodeV4MasqAddrForThisPeer)
}
func (v NodeView) SelfNodeV6MasqAddrForThisPeer() *netip.Addr {
if v.ж.SelfNodeV6MasqAddrForThisPeer == nil {
return nil
}
x := *v.ж.SelfNodeV6MasqAddrForThisPeer
return &x
func (v NodeView) SelfNodeV6MasqAddrForThisPeer() views.ValuePointer[netip.Addr] {
return views.ValuePointerOf(v.ж.SelfNodeV6MasqAddrForThisPeer)
}
func (v NodeView) IsWireGuardOnly() bool { return v.ж.IsWireGuardOnly }
@@ -214,7 +193,8 @@ var _NodeViewNeedsRegeneration = Node(struct {
Addresses []netip.Prefix
AllowedIPs []netip.Prefix
Endpoints []netip.AddrPort
DERP string
LegacyDERPString string
HomeDERP int
Hostinfo HostinfoView
Created time.Time
Cap CapabilityVersion
@@ -238,7 +218,7 @@ var _NodeViewNeedsRegeneration = Node(struct {
ExitNodeDNSResolvers []*dnstype.Resolver
}{})
// View returns a readonly view of Hostinfo.
// View returns a read-only view of Hostinfo.
func (p *Hostinfo) View() HostinfoView {
return HostinfoView{ж: p}
}
@@ -254,7 +234,7 @@ type HostinfoView struct {
ж *Hostinfo
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v HostinfoView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -303,6 +283,7 @@ func (v HostinfoView) ShieldsUp() bool { return v.ж.Shie
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport }
func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress }
func (v HostinfoView) IngressEnabled() bool { return v.ж.IngressEnabled }
func (v HostinfoView) AllowsUpdate() bool { return v.ж.AllowsUpdate }
func (v HostinfoView) Machine() string { return v.ж.Machine }
func (v HostinfoView) GoArch() string { return v.ж.GoArch }
@@ -318,15 +299,9 @@ func (v HostinfoView) Cloud() string { return v.ж.Clou
func (v HostinfoView) Userspace() opt.Bool { return v.ж.Userspace }
func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.UserspaceRouter }
func (v HostinfoView) AppConnector() opt.Bool { return v.ж.AppConnector }
func (v HostinfoView) Location() *Location {
if v.ж.Location == nil {
return nil
}
x := *v.ж.Location
return &x
}
func (v HostinfoView) Equal(v2 HostinfoView) bool { return v.ж.Equal(v2.ж) }
func (v HostinfoView) ServicesHash() string { return v.ж.ServicesHash }
func (v HostinfoView) Location() LocationView { return v.ж.Location.View() }
func (v HostinfoView) Equal(v2 HostinfoView) bool { return v.ж.Equal(v2.ж) }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
@@ -350,6 +325,7 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
ShareeNode bool
NoLogsNoSupport bool
WireIngress bool
IngressEnabled bool
AllowsUpdate bool
Machine string
GoArch string
@@ -365,10 +341,11 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
Userspace opt.Bool
UserspaceRouter opt.Bool
AppConnector opt.Bool
ServicesHash string
Location *Location
}{})
// View returns a readonly view of NetInfo.
// View returns a read-only view of NetInfo.
func (p *NetInfo) View() NetInfoView {
return NetInfoView{ж: p}
}
@@ -384,7 +361,7 @@ type NetInfoView struct {
ж *NetInfo
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v NetInfoView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -448,7 +425,7 @@ var _NetInfoViewNeedsRegeneration = NetInfo(struct {
FirewallMode string
}{})
// View returns a readonly view of Login.
// View returns a read-only view of Login.
func (p *Login) View() LoginView {
return LoginView{ж: p}
}
@@ -464,7 +441,7 @@ type LoginView struct {
ж *Login
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v LoginView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -509,7 +486,7 @@ var _LoginViewNeedsRegeneration = Login(struct {
ProfilePicURL string
}{})
// View returns a readonly view of DNSConfig.
// View returns a read-only view of DNSConfig.
func (p *DNSConfig) View() DNSConfigView {
return DNSConfigView{ж: p}
}
@@ -525,7 +502,7 @@ type DNSConfigView struct {
ж *DNSConfig
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v DNSConfigView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -590,7 +567,7 @@ var _DNSConfigViewNeedsRegeneration = DNSConfig(struct {
TempCorpIssue13969 string
}{})
// View returns a readonly view of RegisterResponse.
// View returns a read-only view of RegisterResponse.
func (p *RegisterResponse) View() RegisterResponseView {
return RegisterResponseView{ж: p}
}
@@ -606,7 +583,7 @@ type RegisterResponseView struct {
ж *RegisterResponse
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v RegisterResponseView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -635,7 +612,7 @@ func (v *RegisterResponseView) UnmarshalJSON(b []byte) error {
return nil
}
func (v RegisterResponseView) User() UserView { return v.ж.User.View() }
func (v RegisterResponseView) User() User { return v.ж.User }
func (v RegisterResponseView) Login() Login { return v.ж.Login }
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
@@ -656,7 +633,7 @@ var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct {
Error string
}{})
// View returns a readonly view of RegisterResponseAuth.
// View returns a read-only view of RegisterResponseAuth.
func (p *RegisterResponseAuth) View() RegisterResponseAuthView {
return RegisterResponseAuthView{ж: p}
}
@@ -672,7 +649,7 @@ type RegisterResponseAuthView struct {
ж *RegisterResponseAuth
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v RegisterResponseAuthView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -701,12 +678,8 @@ func (v *RegisterResponseAuthView) UnmarshalJSON(b []byte) error {
return nil
}
func (v RegisterResponseAuthView) Oauth2Token() *Oauth2Token {
if v.ж.Oauth2Token == nil {
return nil
}
x := *v.ж.Oauth2Token
return &x
func (v RegisterResponseAuthView) Oauth2Token() views.ValuePointer[Oauth2Token] {
return views.ValuePointerOf(v.ж.Oauth2Token)
}
func (v RegisterResponseAuthView) AuthKey() string { return v.ж.AuthKey }
@@ -718,7 +691,7 @@ var _RegisterResponseAuthViewNeedsRegeneration = RegisterResponseAuth(struct {
AuthKey string
}{})
// View returns a readonly view of RegisterRequest.
// View returns a read-only view of RegisterRequest.
func (p *RegisterRequest) View() RegisterRequestView {
return RegisterRequestView{ж: p}
}
@@ -734,7 +707,7 @@ type RegisterRequestView struct {
ж *RegisterRequest
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v RegisterRequestView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -776,12 +749,8 @@ func (v RegisterRequestView) NodeKeySignature() views.ByteSlice[tkatype.Marshale
return views.ByteSliceOf(v.ж.NodeKeySignature)
}
func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType }
func (v RegisterRequestView) Timestamp() *time.Time {
if v.ж.Timestamp == nil {
return nil
}
x := *v.ж.Timestamp
return &x
func (v RegisterRequestView) Timestamp() views.ValuePointer[time.Time] {
return views.ValuePointerOf(v.ж.Timestamp)
}
func (v RegisterRequestView) DeviceCert() views.ByteSlice[[]byte] {
@@ -812,7 +781,7 @@ var _RegisterRequestViewNeedsRegeneration = RegisterRequest(struct {
Tailnet string
}{})
// View returns a readonly view of DERPHomeParams.
// View returns a read-only view of DERPHomeParams.
func (p *DERPHomeParams) View() DERPHomeParamsView {
return DERPHomeParamsView{ж: p}
}
@@ -828,7 +797,7 @@ type DERPHomeParamsView struct {
ж *DERPHomeParams
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v DERPHomeParamsView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -866,7 +835,7 @@ var _DERPHomeParamsViewNeedsRegeneration = DERPHomeParams(struct {
RegionScore map[int]float64
}{})
// View returns a readonly view of DERPRegion.
// View returns a read-only view of DERPRegion.
func (p *DERPRegion) View() DERPRegionView {
return DERPRegionView{ж: p}
}
@@ -882,7 +851,7 @@ type DERPRegionView struct {
ж *DERPRegion
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v DERPRegionView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -911,28 +880,30 @@ func (v *DERPRegionView) UnmarshalJSON(b []byte) error {
return nil
}
func (v DERPRegionView) RegionID() int { return v.ж.RegionID }
func (v DERPRegionView) RegionCode() string { return v.ж.RegionCode }
func (v DERPRegionView) RegionName() string { return v.ж.RegionName }
func (v DERPRegionView) Latitude() float64 { return v.ж.Latitude }
func (v DERPRegionView) Longitude() float64 { return v.ж.Longitude }
func (v DERPRegionView) Avoid() bool { return v.ж.Avoid }
func (v DERPRegionView) RegionID() int { return v.ж.RegionID }
func (v DERPRegionView) RegionCode() string { return v.ж.RegionCode }
func (v DERPRegionView) RegionName() string { return v.ж.RegionName }
func (v DERPRegionView) Latitude() float64 { return v.ж.Latitude }
func (v DERPRegionView) Longitude() float64 { return v.ж.Longitude }
func (v DERPRegionView) Avoid() bool { return v.ж.Avoid }
func (v DERPRegionView) NoMeasureNoHome() bool { return v.ж.NoMeasureNoHome }
func (v DERPRegionView) Nodes() views.SliceView[*DERPNode, DERPNodeView] {
return views.SliceOfViews[*DERPNode, DERPNodeView](v.ж.Nodes)
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _DERPRegionViewNeedsRegeneration = DERPRegion(struct {
RegionID int
RegionCode string
RegionName string
Latitude float64
Longitude float64
Avoid bool
Nodes []*DERPNode
RegionID int
RegionCode string
RegionName string
Latitude float64
Longitude float64
Avoid bool
NoMeasureNoHome bool
Nodes []*DERPNode
}{})
// View returns a readonly view of DERPMap.
// View returns a read-only view of DERPMap.
func (p *DERPMap) View() DERPMapView {
return DERPMapView{ж: p}
}
@@ -948,7 +919,7 @@ type DERPMapView struct {
ж *DERPMap
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v DERPMapView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -993,7 +964,7 @@ var _DERPMapViewNeedsRegeneration = DERPMap(struct {
OmitDefaultRegions bool
}{})
// View returns a readonly view of DERPNode.
// View returns a read-only view of DERPNode.
func (p *DERPNode) View() DERPNodeView {
return DERPNodeView{ж: p}
}
@@ -1009,7 +980,7 @@ type DERPNodeView struct {
ж *DERPNode
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v DERPNodeView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1067,7 +1038,7 @@ var _DERPNodeViewNeedsRegeneration = DERPNode(struct {
CanPort80 bool
}{})
// View returns a readonly view of SSHRule.
// View returns a read-only view of SSHRule.
func (p *SSHRule) View() SSHRuleView {
return SSHRuleView{ж: p}
}
@@ -1083,7 +1054,7 @@ type SSHRuleView struct {
ж *SSHRule
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v SSHRuleView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1112,12 +1083,8 @@ func (v *SSHRuleView) UnmarshalJSON(b []byte) error {
return nil
}
func (v SSHRuleView) RuleExpires() *time.Time {
if v.ж.RuleExpires == nil {
return nil
}
x := *v.ж.RuleExpires
return &x
func (v SSHRuleView) RuleExpires() views.ValuePointer[time.Time] {
return views.ValuePointerOf(v.ж.RuleExpires)
}
func (v SSHRuleView) Principals() views.SliceView[*SSHPrincipal, SSHPrincipalView] {
@@ -1137,7 +1104,7 @@ var _SSHRuleViewNeedsRegeneration = SSHRule(struct {
AcceptEnv []string
}{})
// View returns a readonly view of SSHAction.
// View returns a read-only view of SSHAction.
func (p *SSHAction) View() SSHActionView {
return SSHActionView{ж: p}
}
@@ -1153,7 +1120,7 @@ type SSHActionView struct {
ж *SSHAction
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v SSHActionView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1191,12 +1158,8 @@ func (v SSHActionView) HoldAndDelegate() string { return v.ж.Hol
func (v SSHActionView) AllowLocalPortForwarding() bool { return v.ж.AllowLocalPortForwarding }
func (v SSHActionView) AllowRemotePortForwarding() bool { return v.ж.AllowRemotePortForwarding }
func (v SSHActionView) Recorders() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Recorders) }
func (v SSHActionView) OnRecordingFailure() *SSHRecorderFailureAction {
if v.ж.OnRecordingFailure == nil {
return nil
}
x := *v.ж.OnRecordingFailure
return &x
func (v SSHActionView) OnRecordingFailure() views.ValuePointer[SSHRecorderFailureAction] {
return views.ValuePointerOf(v.ж.OnRecordingFailure)
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
@@ -1213,7 +1176,7 @@ var _SSHActionViewNeedsRegeneration = SSHAction(struct {
OnRecordingFailure *SSHRecorderFailureAction
}{})
// View returns a readonly view of SSHPrincipal.
// View returns a read-only view of SSHPrincipal.
func (p *SSHPrincipal) View() SSHPrincipalView {
return SSHPrincipalView{ж: p}
}
@@ -1229,7 +1192,7 @@ type SSHPrincipalView struct {
ж *SSHPrincipal
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v SSHPrincipalView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1258,22 +1221,24 @@ func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error {
return nil
}
func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node }
func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP }
func (v SSHPrincipalView) UserLogin() string { return v.ж.UserLogin }
func (v SSHPrincipalView) Any() bool { return v.ж.Any }
func (v SSHPrincipalView) PubKeys() views.Slice[string] { return views.SliceOf(v.ж.PubKeys) }
func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node }
func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP }
func (v SSHPrincipalView) UserLogin() string { return v.ж.UserLogin }
func (v SSHPrincipalView) Any() bool { return v.ж.Any }
func (v SSHPrincipalView) UnusedPubKeys() views.Slice[string] {
return views.SliceOf(v.ж.UnusedPubKeys)
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _SSHPrincipalViewNeedsRegeneration = SSHPrincipal(struct {
Node StableNodeID
NodeIP string
UserLogin string
Any bool
PubKeys []string
Node StableNodeID
NodeIP string
UserLogin string
Any bool
UnusedPubKeys []string
}{})
// View returns a readonly view of ControlDialPlan.
// View returns a read-only view of ControlDialPlan.
func (p *ControlDialPlan) View() ControlDialPlanView {
return ControlDialPlanView{ж: p}
}
@@ -1289,7 +1254,7 @@ type ControlDialPlanView struct {
ж *ControlDialPlan
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v ControlDialPlanView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1327,7 +1292,7 @@ var _ControlDialPlanViewNeedsRegeneration = ControlDialPlan(struct {
Candidates []ControlIPCandidate
}{})
// View returns a readonly view of Location.
// View returns a read-only view of Location.
func (p *Location) View() LocationView {
return LocationView{ж: p}
}
@@ -1343,7 +1308,7 @@ type LocationView struct {
ж *Location
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v LocationView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1391,7 +1356,7 @@ var _LocationViewNeedsRegeneration = Location(struct {
Priority int
}{})
// View returns a readonly view of UserProfile.
// View returns a read-only view of UserProfile.
func (p *UserProfile) View() UserProfileView {
return UserProfileView{ж: p}
}
@@ -1407,7 +1372,7 @@ type UserProfileView struct {
ж *UserProfile
}
// Valid reports whether underlying value is non-nil.
// Valid reports whether v's underlying value is non-nil.
func (v UserProfileView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
@@ -1440,7 +1405,6 @@ func (v UserProfileView) ID() UserID { return v.ж.ID }
func (v UserProfileView) LoginName() string { return v.ж.LoginName }
func (v UserProfileView) DisplayName() string { return v.ж.DisplayName }
func (v UserProfileView) ProfilePicURL() string { return v.ж.ProfilePicURL }
func (v UserProfileView) Roles() emptyStructJSONSlice { return v.ж.Roles }
func (v UserProfileView) Equal(v2 UserProfileView) bool { return v.ж.Equal(v2.ж) }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
@@ -1449,5 +1413,4 @@ var _UserProfileViewNeedsRegeneration = UserProfile(struct {
LoginName string
DisplayName string
ProfilePicURL string
Roles emptyStructJSONSlice
}{})