|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
// Code generated by tailscale/cmd/viewer; DO NOT EDIT.
|
|
|
|
|
|
package tailcfg
|
|
|
|
|
|
import (
|
|
|
jsonv1 "encoding/json"
|
|
|
"errors"
|
|
|
"net/netip"
|
|
|
"time"
|
|
|
|
|
|
jsonv2 "github.com/go-json-experiment/json"
|
|
|
"github.com/go-json-experiment/json/jsontext"
|
|
|
"tailscale.com/types/dnstype"
|
|
|
"tailscale.com/types/key"
|
|
|
"tailscale.com/types/opt"
|
|
|
"tailscale.com/types/structs"
|
|
|
"tailscale.com/types/tkatype"
|
|
|
"tailscale.com/types/views"
|
|
|
)
|
|
|
|
|
|
//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,VIPService,SSHPolicy
|
|
|
|
|
|
// View returns a read-only view of User.
|
|
|
func (p *User) View() UserView {
|
|
|
return UserView{ж: p}
|
|
|
}
|
|
|
|
|
|
// UserView provides a read-only view over User.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type UserView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *User
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v UserView) AsStruct() *User {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v UserView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v UserView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *UserView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x User
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *UserView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x User
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (v UserView) ID() UserID { return v.ж.ID }
|
|
|
|
|
|
// if non-empty overrides Login field
|
|
|
func (v UserView) DisplayName() string { return v.ж.DisplayName }
|
|
|
|
|
|
// if non-empty overrides Login field
|
|
|
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
|
|
|
DisplayName string
|
|
|
ProfilePicURL string
|
|
|
Created time.Time
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of Node.
|
|
|
func (p *Node) View() NodeView {
|
|
|
return NodeView{ж: p}
|
|
|
}
|
|
|
|
|
|
// NodeView provides a read-only view over Node.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type NodeView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *Node
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v NodeView) AsStruct() *Node {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v NodeView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v NodeView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *NodeView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x Node
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *NodeView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x Node
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (v NodeView) ID() NodeID { return v.ж.ID }
|
|
|
func (v NodeView) StableID() StableNodeID { return v.ж.StableID }
|
|
|
|
|
|
// Name is the FQDN of the node.
|
|
|
// It is also the MagicDNS name for the node.
|
|
|
// It has a trailing dot.
|
|
|
// e.g. "host.tail-scale.ts.net."
|
|
|
func (v NodeView) Name() string { return v.ж.Name }
|
|
|
|
|
|
// User is the user who created the node. If ACL tags are in use for the
|
|
|
// node then it doesn't reflect the ACL identity that the node is running
|
|
|
// as.
|
|
|
func (v NodeView) User() UserID { return v.ж.User }
|
|
|
|
|
|
// Sharer, if non-zero, is the user who shared this node, if different than User.
|
|
|
func (v NodeView) Sharer() UserID { return v.ж.Sharer }
|
|
|
func (v NodeView) Key() key.NodePublic { return v.ж.Key }
|
|
|
|
|
|
// the zero value if this node does not expire
|
|
|
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
|
|
|
func (v NodeView) KeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
|
|
|
return views.ByteSliceOf(v.ж.KeySignature)
|
|
|
}
|
|
|
func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine }
|
|
|
func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey }
|
|
|
|
|
|
// Addresses are the IP addresses of this Node directly.
|
|
|
func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) }
|
|
|
|
|
|
// 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.
|
|
|
func (v NodeView) AllowedIPs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.AllowedIPs) }
|
|
|
|
|
|
// IP+port (public via STUN, and local LANs)
|
|
|
func (v NodeView) Endpoints() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Endpoints) }
|
|
|
|
|
|
// 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 LegacyDERPString region ID
|
|
|
// integer.
|
|
|
//
|
|
|
// 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.
|
|
|
func (v NodeView) LegacyDERPString() string { return v.ж.LegacyDERPString }
|
|
|
|
|
|
// 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.
|
|
|
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 }
|
|
|
|
|
|
// if non-zero, the node's capability version; old servers might not send
|
|
|
func (v NodeView) Cap() CapabilityVersion { return v.ж.Cap }
|
|
|
|
|
|
// Tags are the list of ACL tags applied to this node.
|
|
|
// Tags take the form of `tag:<value>` where value starts
|
|
|
// with a letter and only contains alphanumerics and dashes `-`.
|
|
|
// Some valid tag examples:
|
|
|
//
|
|
|
// `tag:prod`
|
|
|
// `tag:database`
|
|
|
// `tag:lab-1`
|
|
|
func (v NodeView) Tags() views.Slice[string] { return views.SliceOf(v.ж.Tags) }
|
|
|
|
|
|
// PrimaryRoutes are the routes from AllowedIPs that this node
|
|
|
// is currently the primary subnet router for, as determined
|
|
|
// by the control plane. It does not include the self address
|
|
|
// values from Addresses that are in AllowedIPs.
|
|
|
func (v NodeView) PrimaryRoutes() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.PrimaryRoutes) }
|
|
|
|
|
|
// LastSeen is when the node was last online. It is not
|
|
|
// updated when Online is true. It is nil if the current
|
|
|
// node doesn't have permission to know, or the node
|
|
|
// has never been online.
|
|
|
func (v NodeView) LastSeen() views.ValuePointer[time.Time] {
|
|
|
return views.ValuePointerOf(v.ж.LastSeen)
|
|
|
}
|
|
|
|
|
|
// Online is whether the node is currently connected to the
|
|
|
// coordination server. A value of nil means unknown, or the
|
|
|
// current node doesn't have permission to know.
|
|
|
func (v NodeView) Online() views.ValuePointer[bool] { return views.ValuePointerOf(v.ж.Online) }
|
|
|
|
|
|
// TODO(crawshaw): replace with MachineStatus
|
|
|
func (v NodeView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
|
|
|
|
|
// Capabilities are capabilities that the node has.
|
|
|
// They're free-form strings, but should be in the form of URLs/URIs
|
|
|
// such as:
|
|
|
//
|
|
|
// "https://tailscale.com/cap/is-admin"
|
|
|
// "https://tailscale.com/cap/file-sharing"
|
|
|
//
|
|
|
// Deprecated: use CapMap instead. See https://github.com/tailscale/tailscale/issues/11508
|
|
|
func (v NodeView) Capabilities() views.Slice[NodeCapability] { return views.SliceOf(v.ж.Capabilities) }
|
|
|
|
|
|
// CapMap is a map of capabilities to their optional argument/data values.
|
|
|
//
|
|
|
// It is valid for a capability to not have any argument/data values; such
|
|
|
// capabilities can be tested for using the HasCap method. These type of
|
|
|
// capabilities are used to indicate that a node has a capability, but there
|
|
|
// is no additional data associated with it. These were previously
|
|
|
// represented by the Capabilities field, but can now be represented by
|
|
|
// CapMap with an empty value.
|
|
|
//
|
|
|
// See NodeCapability for more information on keys.
|
|
|
//
|
|
|
// Metadata about nodes can be transmitted in 3 ways:
|
|
|
// 1. MapResponse.Node.CapMap describes attributes that affect behavior for
|
|
|
// this node, such as which features have been enabled through the admin
|
|
|
// panel and any associated configuration details.
|
|
|
// 2. MapResponse.PacketFilter(s) describes access (both IP and application
|
|
|
// based) that should be granted to peers.
|
|
|
// 3. MapResponse.Peers[].CapMap describes attributes regarding a peer node,
|
|
|
// such as which features the peer supports or if that peer is preferred
|
|
|
// for a particular task vs other peers that could also be chosen.
|
|
|
func (v NodeView) CapMap() views.MapSlice[NodeCapability, RawMessage] {
|
|
|
return views.MapSliceOf(v.ж.CapMap)
|
|
|
}
|
|
|
|
|
|
// UnsignedPeerAPIOnly means that this node is not signed nor subject to TKA
|
|
|
// restrictions. However, in exchange for that privilege, it does not get
|
|
|
// network access. It can only access this node's peerapi, which may not let
|
|
|
// it do anything. It is the tailscaled client's job to double-check the
|
|
|
// MapResponse's PacketFilter to verify that its AllowedIPs will not be
|
|
|
// accepted by the packet filter.
|
|
|
func (v NodeView) UnsignedPeerAPIOnly() bool { return v.ж.UnsignedPeerAPIOnly }
|
|
|
|
|
|
// MagicDNS base name (for normal non-shared-in nodes), FQDN (without trailing dot, for shared-in nodes), or Hostname (if no MagicDNS)
|
|
|
func (v NodeView) ComputedName() string { return v.ж.ComputedName }
|
|
|
|
|
|
// either "ComputedName" or "ComputedName (computedHostIfDifferent)", if computedHostIfDifferent is set
|
|
|
func (v NodeView) ComputedNameWithHost() string { return v.ж.ComputedNameWithHost }
|
|
|
|
|
|
// DataPlaneAuditLogID is the per-node logtail ID used for data plane audit logging.
|
|
|
func (v NodeView) DataPlaneAuditLogID() string { return v.ж.DataPlaneAuditLogID }
|
|
|
|
|
|
// Expired is whether this node's key has expired. Control may send
|
|
|
// this; clients are only allowed to set this from false to true. On
|
|
|
// the client, this is calculated client-side based on a timestamp sent
|
|
|
// from control, to avoid clock skew issues.
|
|
|
func (v NodeView) Expired() bool { return v.ж.Expired }
|
|
|
|
|
|
// SelfNodeV4MasqAddrForThisPeer is the IPv4 that this peer knows the current node as.
|
|
|
// It may be empty if the peer knows the current node by its native
|
|
|
// IPv4 address.
|
|
|
// This field is only populated in a MapResponse for peers and not
|
|
|
// for the current node.
|
|
|
//
|
|
|
// If set, it should be used to masquerade traffic originating from the
|
|
|
// current node to this peer. The masquerade address is only relevant
|
|
|
// for this peer and not for other peers.
|
|
|
//
|
|
|
// This only applies to traffic originating from the current node to the
|
|
|
// peer or any of its subnets. Traffic originating from subnet routes will
|
|
|
// not be masqueraded (e.g. in case of --snat-subnet-routes).
|
|
|
func (v NodeView) SelfNodeV4MasqAddrForThisPeer() views.ValuePointer[netip.Addr] {
|
|
|
return views.ValuePointerOf(v.ж.SelfNodeV4MasqAddrForThisPeer)
|
|
|
}
|
|
|
|
|
|
// SelfNodeV6MasqAddrForThisPeer is the IPv6 that this peer knows the current node as.
|
|
|
// It may be empty if the peer knows the current node by its native
|
|
|
// IPv6 address.
|
|
|
// This field is only populated in a MapResponse for peers and not
|
|
|
// for the current node.
|
|
|
//
|
|
|
// If set, it should be used to masquerade traffic originating from the
|
|
|
// current node to this peer. The masquerade address is only relevant
|
|
|
// for this peer and not for other peers.
|
|
|
//
|
|
|
// This only applies to traffic originating from the current node to the
|
|
|
// peer or any of its subnets. Traffic originating from subnet routes will
|
|
|
// not be masqueraded (e.g. in case of --snat-subnet-routes).
|
|
|
func (v NodeView) SelfNodeV6MasqAddrForThisPeer() views.ValuePointer[netip.Addr] {
|
|
|
return views.ValuePointerOf(v.ж.SelfNodeV6MasqAddrForThisPeer)
|
|
|
}
|
|
|
|
|
|
// IsWireGuardOnly indicates that this is a non-Tailscale WireGuard peer, it
|
|
|
// is not expected to speak Disco or DERP, and it must have Endpoints in
|
|
|
// order to be reachable.
|
|
|
func (v NodeView) IsWireGuardOnly() bool { return v.ж.IsWireGuardOnly }
|
|
|
|
|
|
// IsJailed indicates that this node is jailed and should not be allowed
|
|
|
// initiate connections, however outbound connections to it should still be
|
|
|
// allowed.
|
|
|
func (v NodeView) IsJailed() bool { return v.ж.IsJailed }
|
|
|
|
|
|
// ExitNodeDNSResolvers is the list of DNS servers that should be used when this
|
|
|
// node is marked IsWireGuardOnly and being used as an exit node.
|
|
|
func (v NodeView) ExitNodeDNSResolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] {
|
|
|
return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.ExitNodeDNSResolvers)
|
|
|
}
|
|
|
func (v NodeView) Equal(v2 NodeView) 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 _NodeViewNeedsRegeneration = Node(struct {
|
|
|
ID NodeID
|
|
|
StableID StableNodeID
|
|
|
Name string
|
|
|
User UserID
|
|
|
Sharer UserID
|
|
|
Key key.NodePublic
|
|
|
KeyExpiry time.Time
|
|
|
KeySignature tkatype.MarshaledSignature
|
|
|
Machine key.MachinePublic
|
|
|
DiscoKey key.DiscoPublic
|
|
|
Addresses []netip.Prefix
|
|
|
AllowedIPs []netip.Prefix
|
|
|
Endpoints []netip.AddrPort
|
|
|
LegacyDERPString string
|
|
|
HomeDERP int
|
|
|
Hostinfo HostinfoView
|
|
|
Created time.Time
|
|
|
Cap CapabilityVersion
|
|
|
Tags []string
|
|
|
PrimaryRoutes []netip.Prefix
|
|
|
LastSeen *time.Time
|
|
|
Online *bool
|
|
|
MachineAuthorized bool
|
|
|
Capabilities []NodeCapability
|
|
|
CapMap NodeCapMap
|
|
|
UnsignedPeerAPIOnly bool
|
|
|
ComputedName string
|
|
|
computedHostIfDifferent string
|
|
|
ComputedNameWithHost string
|
|
|
DataPlaneAuditLogID string
|
|
|
Expired bool
|
|
|
SelfNodeV4MasqAddrForThisPeer *netip.Addr
|
|
|
SelfNodeV6MasqAddrForThisPeer *netip.Addr
|
|
|
IsWireGuardOnly bool
|
|
|
IsJailed bool
|
|
|
ExitNodeDNSResolvers []*dnstype.Resolver
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of Hostinfo.
|
|
|
func (p *Hostinfo) View() HostinfoView {
|
|
|
return HostinfoView{ж: p}
|
|
|
}
|
|
|
|
|
|
// HostinfoView provides a read-only view over Hostinfo.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type HostinfoView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *Hostinfo
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v HostinfoView) AsStruct() *Hostinfo {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v HostinfoView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v HostinfoView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *HostinfoView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x Hostinfo
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *HostinfoView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x Hostinfo
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// version of this code (in version.Long format)
|
|
|
func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion }
|
|
|
|
|
|
// logtail ID of frontend instance
|
|
|
func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID }
|
|
|
|
|
|
// logtail ID of backend instance
|
|
|
func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID }
|
|
|
|
|
|
// operating system the client runs on (a version.OS value)
|
|
|
func (v HostinfoView) OS() string { return v.ж.OS }
|
|
|
|
|
|
// OSVersion is the version of the OS, if available.
|
|
|
//
|
|
|
// For Android, it's like "10", "11", "12", etc. For iOS and macOS it's like
|
|
|
// "15.6.1" or "12.4.0". For Windows it's like "10.0.19044.1889". For
|
|
|
// FreeBSD it's like "12.3-STABLE".
|
|
|
//
|
|
|
// For Linux, prior to Tailscale 1.32, we jammed a bunch of fields into this
|
|
|
// string on Linux, like "Debian 10.4; kernel=xxx; container; env=kn" and so
|
|
|
// on. As of Tailscale 1.32, this is simply the kernel version on Linux, like
|
|
|
// "5.10.0-17-amd64".
|
|
|
func (v HostinfoView) OSVersion() string { return v.ж.OSVersion }
|
|
|
|
|
|
// best-effort whether the client is running in a container
|
|
|
func (v HostinfoView) Container() opt.Bool { return v.ж.Container }
|
|
|
|
|
|
// a hostinfo.EnvType in string form
|
|
|
func (v HostinfoView) Env() string { return v.ж.Env }
|
|
|
|
|
|
// "debian", "ubuntu", "nixos", ...
|
|
|
func (v HostinfoView) Distro() string { return v.ж.Distro }
|
|
|
|
|
|
// "20.04", ...
|
|
|
func (v HostinfoView) DistroVersion() string { return v.ж.DistroVersion }
|
|
|
|
|
|
// "jammy", "bullseye", ...
|
|
|
func (v HostinfoView) DistroCodeName() string { return v.ж.DistroCodeName }
|
|
|
|
|
|
// App is used to disambiguate Tailscale clients that run using tsnet.
|
|
|
func (v HostinfoView) App() string { return v.ж.App }
|
|
|
|
|
|
// if a desktop was detected on Linux
|
|
|
func (v HostinfoView) Desktop() opt.Bool { return v.ж.Desktop }
|
|
|
|
|
|
// Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
|
|
|
func (v HostinfoView) Package() string { return v.ж.Package }
|
|
|
|
|
|
// mobile phone model ("Pixel 3a", "iPhone12,3")
|
|
|
func (v HostinfoView) DeviceModel() string { return v.ж.DeviceModel }
|
|
|
|
|
|
// macOS/iOS APNs device token for notifications (and Android in the future)
|
|
|
func (v HostinfoView) PushDeviceToken() string { return v.ж.PushDeviceToken }
|
|
|
|
|
|
// name of the host the client runs on
|
|
|
func (v HostinfoView) Hostname() string { return v.ж.Hostname }
|
|
|
|
|
|
// indicates whether the host is blocking incoming connections
|
|
|
func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp }
|
|
|
|
|
|
// indicates this node exists in netmap because it's owned by a shared-to user
|
|
|
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
|
|
|
|
|
|
// indicates that the user has opted out of sending logs and support
|
|
|
func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport }
|
|
|
|
|
|
// 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.
|
|
|
func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress }
|
|
|
|
|
|
// if the node has any funnel endpoint enabled
|
|
|
func (v HostinfoView) IngressEnabled() bool { return v.ж.IngressEnabled }
|
|
|
|
|
|
// indicates that the node has opted-in to admin-console-drive remote updates
|
|
|
func (v HostinfoView) AllowsUpdate() bool { return v.ж.AllowsUpdate }
|
|
|
|
|
|
// the current host's machine type (uname -m)
|
|
|
func (v HostinfoView) Machine() string { return v.ж.Machine }
|
|
|
|
|
|
// GOARCH value (of the built binary)
|
|
|
func (v HostinfoView) GoArch() string { return v.ж.GoArch }
|
|
|
|
|
|
// GOARM, GOAMD64, etc (of the built binary)
|
|
|
func (v HostinfoView) GoArchVar() string { return v.ж.GoArchVar }
|
|
|
|
|
|
// Go version binary was built with
|
|
|
func (v HostinfoView) GoVersion() string { return v.ж.GoVersion }
|
|
|
|
|
|
// set of IP ranges this client can route
|
|
|
func (v HostinfoView) RoutableIPs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.RoutableIPs) }
|
|
|
|
|
|
// set of ACL tags this node wants to claim
|
|
|
func (v HostinfoView) RequestTags() views.Slice[string] { return views.SliceOf(v.ж.RequestTags) }
|
|
|
|
|
|
// MAC address(es) to send Wake-on-LAN packets to wake this node (lowercase hex w/ colons)
|
|
|
func (v HostinfoView) WoLMACs() views.Slice[string] { return views.SliceOf(v.ж.WoLMACs) }
|
|
|
|
|
|
// services advertised by this machine
|
|
|
func (v HostinfoView) Services() views.Slice[Service] { return views.SliceOf(v.ж.Services) }
|
|
|
func (v HostinfoView) NetInfo() NetInfoView { return v.ж.NetInfo.View() }
|
|
|
|
|
|
// if advertised
|
|
|
func (v HostinfoView) SSH_HostKeys() views.Slice[string] { return views.SliceOf(v.ж.SSH_HostKeys) }
|
|
|
func (v HostinfoView) Cloud() string { return v.ж.Cloud }
|
|
|
|
|
|
// if the client is running in userspace (netstack) mode
|
|
|
func (v HostinfoView) Userspace() opt.Bool { return v.ж.Userspace }
|
|
|
|
|
|
// if the client's subnet router is running in userspace (netstack) mode
|
|
|
func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.UserspaceRouter }
|
|
|
|
|
|
// if the client is running the app-connector service
|
|
|
func (v HostinfoView) AppConnector() opt.Bool { return v.ж.AppConnector }
|
|
|
|
|
|
// opaque hash of the most recent list of tailnet services, change in hash indicates config should be fetched via c2n
|
|
|
func (v HostinfoView) ServicesHash() string { return v.ж.ServicesHash }
|
|
|
|
|
|
// the client’s selected exit node, empty when unselected.
|
|
|
func (v HostinfoView) ExitNodeID() StableNodeID { return v.ж.ExitNodeID }
|
|
|
|
|
|
// Location represents geographical location data about a
|
|
|
// Tailscale host. Location is optional and only set if
|
|
|
// explicitly declared by a node.
|
|
|
func (v HostinfoView) Location() LocationView { return v.ж.Location.View() }
|
|
|
|
|
|
// TPM device metadata, if available
|
|
|
func (v HostinfoView) TPM() views.ValuePointer[TPMInfo] { return views.ValuePointerOf(v.ж.TPM) }
|
|
|
|
|
|
// StateEncrypted reports whether the node state is stored encrypted on
|
|
|
// disk. The actual mechanism is platform-specific:
|
|
|
// - Apple nodes use the Keychain
|
|
|
// - Linux and Windows nodes use the TPM
|
|
|
// - Android apps use EncryptedSharedPreferences
|
|
|
func (v HostinfoView) StateEncrypted() opt.Bool { return v.ж.StateEncrypted }
|
|
|
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 {
|
|
|
IPNVersion string
|
|
|
FrontendLogID string
|
|
|
BackendLogID string
|
|
|
OS string
|
|
|
OSVersion string
|
|
|
Container opt.Bool
|
|
|
Env string
|
|
|
Distro string
|
|
|
DistroVersion string
|
|
|
DistroCodeName string
|
|
|
App string
|
|
|
Desktop opt.Bool
|
|
|
Package string
|
|
|
DeviceModel string
|
|
|
PushDeviceToken string
|
|
|
Hostname string
|
|
|
ShieldsUp bool
|
|
|
ShareeNode bool
|
|
|
NoLogsNoSupport bool
|
|
|
WireIngress bool
|
|
|
IngressEnabled bool
|
|
|
AllowsUpdate bool
|
|
|
Machine string
|
|
|
GoArch string
|
|
|
GoArchVar string
|
|
|
GoVersion string
|
|
|
RoutableIPs []netip.Prefix
|
|
|
RequestTags []string
|
|
|
WoLMACs []string
|
|
|
Services []Service
|
|
|
NetInfo *NetInfo
|
|
|
SSH_HostKeys []string
|
|
|
Cloud string
|
|
|
Userspace opt.Bool
|
|
|
UserspaceRouter opt.Bool
|
|
|
AppConnector opt.Bool
|
|
|
ServicesHash string
|
|
|
ExitNodeID StableNodeID
|
|
|
Location *Location
|
|
|
TPM *TPMInfo
|
|
|
StateEncrypted opt.Bool
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of NetInfo.
|
|
|
func (p *NetInfo) View() NetInfoView {
|
|
|
return NetInfoView{ж: p}
|
|
|
}
|
|
|
|
|
|
// NetInfoView provides a read-only view over NetInfo.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type NetInfoView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *NetInfo
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v NetInfoView) AsStruct() *NetInfo {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v NetInfoView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v NetInfoView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *NetInfoView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x NetInfo
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *NetInfoView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x NetInfo
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// MappingVariesByDestIP says whether the host's NAT mappings
|
|
|
// vary based on the destination IP.
|
|
|
func (v NetInfoView) MappingVariesByDestIP() opt.Bool { return v.ж.MappingVariesByDestIP }
|
|
|
|
|
|
// HairPinning is their router does hairpinning.
|
|
|
// It reports true even if there's no NAT involved.
|
|
|
func (v NetInfoView) HairPinning() opt.Bool { return v.ж.HairPinning }
|
|
|
|
|
|
// WorkingIPv6 is whether the host has IPv6 internet connectivity.
|
|
|
func (v NetInfoView) WorkingIPv6() opt.Bool { return v.ж.WorkingIPv6 }
|
|
|
|
|
|
// OSHasIPv6 is whether the OS supports IPv6 at all, regardless of
|
|
|
// whether IPv6 internet connectivity is available.
|
|
|
func (v NetInfoView) OSHasIPv6() opt.Bool { return v.ж.OSHasIPv6 }
|
|
|
|
|
|
// WorkingUDP is whether the host has UDP internet connectivity.
|
|
|
func (v NetInfoView) WorkingUDP() opt.Bool { return v.ж.WorkingUDP }
|
|
|
|
|
|
// WorkingICMPv4 is whether ICMPv4 works.
|
|
|
// Empty means not checked.
|
|
|
func (v NetInfoView) WorkingICMPv4() opt.Bool { return v.ж.WorkingICMPv4 }
|
|
|
|
|
|
// HavePortMap is whether we have an existing portmap open
|
|
|
// (UPnP, PMP, or PCP).
|
|
|
func (v NetInfoView) HavePortMap() bool { return v.ж.HavePortMap }
|
|
|
|
|
|
// UPnP is whether UPnP appears present on the LAN.
|
|
|
// Empty means not checked.
|
|
|
func (v NetInfoView) UPnP() opt.Bool { return v.ж.UPnP }
|
|
|
|
|
|
// PMP is whether NAT-PMP appears present on the LAN.
|
|
|
// Empty means not checked.
|
|
|
func (v NetInfoView) PMP() opt.Bool { return v.ж.PMP }
|
|
|
|
|
|
// PCP is whether PCP appears present on the LAN.
|
|
|
// Empty means not checked.
|
|
|
func (v NetInfoView) PCP() opt.Bool { return v.ж.PCP }
|
|
|
|
|
|
// PreferredDERP is this node's preferred (home) DERP region ID.
|
|
|
// This is where the node expects to be contacted to begin a
|
|
|
// peer-to-peer connection. The node might be be temporarily
|
|
|
// connected to multiple DERP servers (to speak to other nodes
|
|
|
// that are located elsewhere) but PreferredDERP is the region ID
|
|
|
// that the node subscribes to traffic at.
|
|
|
// Zero means disconnected or unknown.
|
|
|
func (v NetInfoView) PreferredDERP() int { return v.ж.PreferredDERP }
|
|
|
|
|
|
// LinkType is the current link type, if known.
|
|
|
func (v NetInfoView) LinkType() string { return v.ж.LinkType }
|
|
|
|
|
|
// DERPLatency is the fastest recent time to reach various
|
|
|
// DERP STUN servers, in seconds. The map key is the
|
|
|
// "regionID-v4" or "-v6"; it was previously the DERP server's
|
|
|
// STUN host:port.
|
|
|
//
|
|
|
// This should only be updated rarely, or when there's a
|
|
|
// material change, as any change here also gets uploaded to
|
|
|
// the control plane.
|
|
|
func (v NetInfoView) DERPLatency() views.Map[string, float64] { return views.MapOf(v.ж.DERPLatency) }
|
|
|
|
|
|
// FirewallMode encodes both which firewall mode was selected and why.
|
|
|
// It is Linux-specific (at least as of 2023-08-19) and is meant to help
|
|
|
// debug iptables-vs-nftables issues. The string is of the form
|
|
|
// "{nft,ift}-REASON", like "nft-forced" or "ipt-default". Empty means
|
|
|
// either not Linux or a configuration in which the host firewall rules
|
|
|
// are not managed by tailscaled.
|
|
|
func (v NetInfoView) FirewallMode() string { return v.ж.FirewallMode }
|
|
|
func (v NetInfoView) String() string { return v.ж.String() }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _NetInfoViewNeedsRegeneration = NetInfo(struct {
|
|
|
MappingVariesByDestIP opt.Bool
|
|
|
HairPinning opt.Bool
|
|
|
WorkingIPv6 opt.Bool
|
|
|
OSHasIPv6 opt.Bool
|
|
|
WorkingUDP opt.Bool
|
|
|
WorkingICMPv4 opt.Bool
|
|
|
HavePortMap bool
|
|
|
UPnP opt.Bool
|
|
|
PMP opt.Bool
|
|
|
PCP opt.Bool
|
|
|
PreferredDERP int
|
|
|
LinkType string
|
|
|
DERPLatency map[string]float64
|
|
|
FirewallMode string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of Login.
|
|
|
func (p *Login) View() LoginView {
|
|
|
return LoginView{ж: p}
|
|
|
}
|
|
|
|
|
|
// LoginView provides a read-only view over Login.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type LoginView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *Login
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v LoginView) AsStruct() *Login {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v LoginView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v LoginView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *LoginView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x Login
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *LoginView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x Login
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// unused in the Tailscale client
|
|
|
func (v LoginView) ID() LoginID { return v.ж.ID }
|
|
|
|
|
|
// "google", "github", "okta_foo", etc.
|
|
|
func (v LoginView) Provider() string { return v.ж.Provider }
|
|
|
|
|
|
// an email address or "email-ish" string (like alice@github)
|
|
|
func (v LoginView) LoginName() string { return v.ж.LoginName }
|
|
|
|
|
|
// from the IdP
|
|
|
func (v LoginView) DisplayName() string { return v.ж.DisplayName }
|
|
|
|
|
|
// from the IdP
|
|
|
func (v LoginView) ProfilePicURL() string { return v.ж.ProfilePicURL }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _LoginViewNeedsRegeneration = Login(struct {
|
|
|
_ structs.Incomparable
|
|
|
ID LoginID
|
|
|
Provider string
|
|
|
LoginName string
|
|
|
DisplayName string
|
|
|
ProfilePicURL string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of DNSConfig.
|
|
|
func (p *DNSConfig) View() DNSConfigView {
|
|
|
return DNSConfigView{ж: p}
|
|
|
}
|
|
|
|
|
|
// DNSConfigView provides a read-only view over DNSConfig.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type DNSConfigView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *DNSConfig
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v DNSConfigView) AsStruct() *DNSConfig {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v DNSConfigView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v DNSConfigView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *DNSConfigView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x DNSConfig
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *DNSConfigView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x DNSConfig
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// Resolvers are the DNS resolvers to use, in order of preference.
|
|
|
func (v DNSConfigView) Resolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] {
|
|
|
return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.Resolvers)
|
|
|
}
|
|
|
|
|
|
// Routes maps DNS name suffixes to a set of DNS resolvers to
|
|
|
// use. It is used to implement "split DNS" and other advanced DNS
|
|
|
// routing overlays.
|
|
|
//
|
|
|
// Map keys are fully-qualified DNS name suffixes; they may
|
|
|
// optionally contain a trailing dot but no leading dot.
|
|
|
//
|
|
|
// If the value is an empty slice, that means the suffix should still
|
|
|
// be handled by Tailscale's built-in resolver (100.100.100.100), such
|
|
|
// as for the purpose of handling ExtraRecords.
|
|
|
func (v DNSConfigView) Routes() views.MapFn[string, []*dnstype.Resolver, views.SliceView[*dnstype.Resolver, dnstype.ResolverView]] {
|
|
|
return views.MapFnOf(v.ж.Routes, func(t []*dnstype.Resolver) views.SliceView[*dnstype.Resolver, dnstype.ResolverView] {
|
|
|
return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](t)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// FallbackResolvers is like Resolvers, but is only used if a
|
|
|
// split DNS configuration is requested in a configuration that
|
|
|
// doesn't work yet without explicit default resolvers.
|
|
|
// https://github.com/tailscale/tailscale/issues/1743
|
|
|
func (v DNSConfigView) FallbackResolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] {
|
|
|
return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.FallbackResolvers)
|
|
|
}
|
|
|
|
|
|
// Domains are the search domains to use.
|
|
|
// Search domains must be FQDNs, but *without* the trailing dot.
|
|
|
func (v DNSConfigView) Domains() views.Slice[string] { return views.SliceOf(v.ж.Domains) }
|
|
|
|
|
|
// Proxied turns on automatic resolution of hostnames for devices
|
|
|
// in the network map, aka MagicDNS.
|
|
|
// Despite the (legacy) name, does not necessarily cause request
|
|
|
// proxying to be enabled.
|
|
|
func (v DNSConfigView) Proxied() bool { return v.ж.Proxied }
|
|
|
|
|
|
// Nameservers are the IP addresses of the global nameservers to use.
|
|
|
//
|
|
|
// Deprecated: this is only set and used by MapRequest.Version >=9 and <14. Use Resolvers instead.
|
|
|
func (v DNSConfigView) Nameservers() views.Slice[netip.Addr] { return views.SliceOf(v.ж.Nameservers) }
|
|
|
|
|
|
// CertDomains are the set of DNS names for which the control
|
|
|
// plane server will assist with provisioning TLS
|
|
|
// certificates. See SetDNSRequest, which can be used to
|
|
|
// answer dns-01 ACME challenges for e.g. LetsEncrypt.
|
|
|
// These names are FQDNs without trailing periods, and without
|
|
|
// any "_acme-challenge." prefix.
|
|
|
func (v DNSConfigView) CertDomains() views.Slice[string] { return views.SliceOf(v.ж.CertDomains) }
|
|
|
|
|
|
// ExtraRecords contains extra DNS records to add to the
|
|
|
// MagicDNS config.
|
|
|
func (v DNSConfigView) ExtraRecords() views.Slice[DNSRecord] { return views.SliceOf(v.ж.ExtraRecords) }
|
|
|
|
|
|
// ExitNodeFilteredSuffixes are the DNS suffixes that the
|
|
|
// node, when being an exit node DNS proxy, should not answer.
|
|
|
//
|
|
|
// The entries do not contain trailing periods and are always
|
|
|
// all lowercase.
|
|
|
//
|
|
|
// If an entry starts with a period, it's a suffix match (but
|
|
|
// suffix ".a.b" doesn't match "a.b"; a prefix is required).
|
|
|
//
|
|
|
// If an entry does not start with a period, it's an exact
|
|
|
// match.
|
|
|
//
|
|
|
// Matches are case insensitive.
|
|
|
func (v DNSConfigView) ExitNodeFilteredSet() views.Slice[string] {
|
|
|
return views.SliceOf(v.ж.ExitNodeFilteredSet)
|
|
|
}
|
|
|
|
|
|
// TempCorpIssue13969 is a temporary (2023-08-16) field for an internal hack day prototype.
|
|
|
// It contains a user inputed URL that should have a list of domains to be blocked.
|
|
|
// See https://github.com/tailscale/corp/issues/13969.
|
|
|
func (v DNSConfigView) TempCorpIssue13969() string { return v.ж.TempCorpIssue13969 }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _DNSConfigViewNeedsRegeneration = DNSConfig(struct {
|
|
|
Resolvers []*dnstype.Resolver
|
|
|
Routes map[string][]*dnstype.Resolver
|
|
|
FallbackResolvers []*dnstype.Resolver
|
|
|
Domains []string
|
|
|
Proxied bool
|
|
|
Nameservers []netip.Addr
|
|
|
CertDomains []string
|
|
|
ExtraRecords []DNSRecord
|
|
|
ExitNodeFilteredSet []string
|
|
|
TempCorpIssue13969 string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of RegisterResponse.
|
|
|
func (p *RegisterResponse) View() RegisterResponseView {
|
|
|
return RegisterResponseView{ж: p}
|
|
|
}
|
|
|
|
|
|
// RegisterResponseView provides a read-only view over RegisterResponse.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type RegisterResponseView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *RegisterResponse
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v RegisterResponseView) AsStruct() *RegisterResponse {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v RegisterResponseView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v RegisterResponseView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *RegisterResponseView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x RegisterResponse
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *RegisterResponseView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x RegisterResponse
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (v RegisterResponseView) User() User { return v.ж.User }
|
|
|
func (v RegisterResponseView) Login() Login { return v.ж.Login }
|
|
|
|
|
|
// if true, the NodeKey needs to be replaced
|
|
|
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
|
|
|
|
|
|
// TODO(crawshaw): move to using MachineStatus
|
|
|
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
|
|
|
|
|
// if set, authorization pending
|
|
|
func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL }
|
|
|
|
|
|
// If set, this is the current node-key signature that needs to be
|
|
|
// re-signed for the node's new node-key.
|
|
|
func (v RegisterResponseView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
|
|
|
return views.ByteSliceOf(v.ж.NodeKeySignature)
|
|
|
}
|
|
|
|
|
|
// Error indicates that authorization failed. If this is non-empty,
|
|
|
// other status fields should be ignored.
|
|
|
func (v RegisterResponseView) Error() string { return v.ж.Error }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct {
|
|
|
User User
|
|
|
Login Login
|
|
|
NodeKeyExpired bool
|
|
|
MachineAuthorized bool
|
|
|
AuthURL string
|
|
|
NodeKeySignature tkatype.MarshaledSignature
|
|
|
Error string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of RegisterResponseAuth.
|
|
|
func (p *RegisterResponseAuth) View() RegisterResponseAuthView {
|
|
|
return RegisterResponseAuthView{ж: p}
|
|
|
}
|
|
|
|
|
|
// RegisterResponseAuthView provides a read-only view over RegisterResponseAuth.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type RegisterResponseAuthView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *RegisterResponseAuth
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v RegisterResponseAuthView) AsStruct() *RegisterResponseAuth {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v RegisterResponseAuthView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v RegisterResponseAuthView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *RegisterResponseAuthView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x RegisterResponseAuth
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *RegisterResponseAuthView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x RegisterResponseAuth
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// used by pre-1.66 Android only
|
|
|
func (v RegisterResponseAuthView) Oauth2Token() views.ValuePointer[Oauth2Token] {
|
|
|
return views.ValuePointerOf(v.ж.Oauth2Token)
|
|
|
}
|
|
|
|
|
|
func (v RegisterResponseAuthView) AuthKey() string { return v.ж.AuthKey }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _RegisterResponseAuthViewNeedsRegeneration = RegisterResponseAuth(struct {
|
|
|
_ structs.Incomparable
|
|
|
Oauth2Token *Oauth2Token
|
|
|
AuthKey string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of RegisterRequest.
|
|
|
func (p *RegisterRequest) View() RegisterRequestView {
|
|
|
return RegisterRequestView{ж: p}
|
|
|
}
|
|
|
|
|
|
// RegisterRequestView provides a read-only view over RegisterRequest.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type RegisterRequestView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *RegisterRequest
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v RegisterRequestView) AsStruct() *RegisterRequest {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v RegisterRequestView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v RegisterRequestView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *RegisterRequestView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x RegisterRequest
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *RegisterRequestView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x RegisterRequest
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// Version is the client's capabilities when using the Noise
|
|
|
// transport.
|
|
|
//
|
|
|
// When using the original nacl crypto_box transport, the
|
|
|
// value must be 1.
|
|
|
func (v RegisterRequestView) Version() CapabilityVersion { return v.ж.Version }
|
|
|
func (v RegisterRequestView) NodeKey() key.NodePublic { return v.ж.NodeKey }
|
|
|
func (v RegisterRequestView) OldNodeKey() key.NodePublic { return v.ж.OldNodeKey }
|
|
|
func (v RegisterRequestView) NLKey() key.NLPublic { return v.ж.NLKey }
|
|
|
func (v RegisterRequestView) Auth() RegisterResponseAuthView { return v.ж.Auth.View() }
|
|
|
|
|
|
// Expiry optionally specifies the requested key expiry.
|
|
|
// The server policy may override.
|
|
|
// As a special case, if Expiry is in the past and NodeKey is
|
|
|
// the node's current key, the key is expired.
|
|
|
func (v RegisterRequestView) Expiry() time.Time { return v.ж.Expiry }
|
|
|
|
|
|
// response waits until AuthURL is visited
|
|
|
func (v RegisterRequestView) Followup() string { return v.ж.Followup }
|
|
|
func (v RegisterRequestView) Hostinfo() HostinfoView { return v.ж.Hostinfo.View() }
|
|
|
|
|
|
// Ephemeral is whether the client is requesting that this
|
|
|
// node be considered ephemeral and be automatically deleted
|
|
|
// when it stops being active.
|
|
|
func (v RegisterRequestView) Ephemeral() bool { return v.ж.Ephemeral }
|
|
|
|
|
|
// NodeKeySignature is the node's own node-key signature, re-signed
|
|
|
// for its new node key using its network-lock key.
|
|
|
//
|
|
|
// This field is set when the client retries registration after learning
|
|
|
// its NodeKeySignature (which is in need of rotation).
|
|
|
func (v RegisterRequestView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
|
|
|
return views.ByteSliceOf(v.ж.NodeKeySignature)
|
|
|
}
|
|
|
|
|
|
// The following fields are not used for SignatureNone and are required for
|
|
|
// SignatureV1:
|
|
|
func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType }
|
|
|
|
|
|
// creation time of request to prevent replay
|
|
|
func (v RegisterRequestView) Timestamp() views.ValuePointer[time.Time] {
|
|
|
return views.ValuePointerOf(v.ж.Timestamp)
|
|
|
}
|
|
|
|
|
|
// X.509 certificate for client device
|
|
|
func (v RegisterRequestView) DeviceCert() views.ByteSlice[[]byte] {
|
|
|
return views.ByteSliceOf(v.ж.DeviceCert)
|
|
|
}
|
|
|
|
|
|
// as described by SignatureType
|
|
|
func (v RegisterRequestView) Signature() views.ByteSlice[[]byte] {
|
|
|
return views.ByteSliceOf(v.ж.Signature)
|
|
|
}
|
|
|
|
|
|
// Tailnet is an optional identifier specifying the name of the recommended or required
|
|
|
// network that the node should join. Its exact form should not be depended on; new
|
|
|
// forms are coming later. The identifier is generally a domain name (for an organization)
|
|
|
// or e-mail address (for a personal account on a shared e-mail provider). It is the same name
|
|
|
// used by the API, as described in /api.md#tailnet.
|
|
|
// If Tailnet begins with the prefix "required:" then the server should prevent logging in to a different
|
|
|
// network than the one specified. Otherwise, the server should recommend the specified network
|
|
|
// but still permit logging in to other networks.
|
|
|
// If empty, no recommendation is offered to the server and the login page should show all options.
|
|
|
func (v RegisterRequestView) Tailnet() string { return v.ж.Tailnet }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _RegisterRequestViewNeedsRegeneration = RegisterRequest(struct {
|
|
|
_ structs.Incomparable
|
|
|
Version CapabilityVersion
|
|
|
NodeKey key.NodePublic
|
|
|
OldNodeKey key.NodePublic
|
|
|
NLKey key.NLPublic
|
|
|
Auth *RegisterResponseAuth
|
|
|
Expiry time.Time
|
|
|
Followup string
|
|
|
Hostinfo *Hostinfo
|
|
|
Ephemeral bool
|
|
|
NodeKeySignature tkatype.MarshaledSignature
|
|
|
SignatureType SignatureType
|
|
|
Timestamp *time.Time
|
|
|
DeviceCert []byte
|
|
|
Signature []byte
|
|
|
Tailnet string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of DERPHomeParams.
|
|
|
func (p *DERPHomeParams) View() DERPHomeParamsView {
|
|
|
return DERPHomeParamsView{ж: p}
|
|
|
}
|
|
|
|
|
|
// DERPHomeParamsView provides a read-only view over DERPHomeParams.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type DERPHomeParamsView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *DERPHomeParams
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v DERPHomeParamsView) AsStruct() *DERPHomeParams {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v DERPHomeParamsView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v DERPHomeParamsView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *DERPHomeParamsView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x DERPHomeParams
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *DERPHomeParamsView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x DERPHomeParams
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// RegionScore scales latencies of DERP regions by a given scaling
|
|
|
// factor when determining which region to use as the home
|
|
|
// ("preferred") DERP. Scores in the range (0, 1) will cause this
|
|
|
// region to be proportionally more preferred, and scores in the range
|
|
|
// (1, ∞) will penalize a region.
|
|
|
//
|
|
|
// If a region is not present in this map, it is treated as having a
|
|
|
// score of 1.0.
|
|
|
//
|
|
|
// Scores should not be 0 or negative; such scores will be ignored.
|
|
|
//
|
|
|
// A nil map means no change from the previous value (if any); an empty
|
|
|
// non-nil map can be sent to reset all scores back to 1.0.
|
|
|
func (v DERPHomeParamsView) RegionScore() views.Map[int, float64] {
|
|
|
return views.MapOf(v.ж.RegionScore)
|
|
|
}
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _DERPHomeParamsViewNeedsRegeneration = DERPHomeParams(struct {
|
|
|
RegionScore map[int]float64
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of DERPRegion.
|
|
|
func (p *DERPRegion) View() DERPRegionView {
|
|
|
return DERPRegionView{ж: p}
|
|
|
}
|
|
|
|
|
|
// DERPRegionView provides a read-only view over DERPRegion.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type DERPRegionView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *DERPRegion
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v DERPRegionView) AsStruct() *DERPRegion {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v DERPRegionView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v DERPRegionView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *DERPRegionView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x DERPRegion
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *DERPRegionView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x DERPRegion
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// RegionID is a unique integer for a geographic region.
|
|
|
//
|
|
|
// It corresponds to the legacy derpN.tailscale.com hostnames
|
|
|
// used by older clients. (Older clients will continue to resolve
|
|
|
// derpN.tailscale.com when contacting peers, rather than use
|
|
|
// the server-provided DERPMap)
|
|
|
//
|
|
|
// RegionIDs must be non-zero, positive, and guaranteed to fit
|
|
|
// in a JavaScript number.
|
|
|
//
|
|
|
// RegionIDs in range 900-999 are reserved for end users to run their
|
|
|
// own DERP nodes.
|
|
|
func (v DERPRegionView) RegionID() int { return v.ж.RegionID }
|
|
|
|
|
|
// RegionCode is a short name for the region. It's usually a popular
|
|
|
// city or airport code in the region: "nyc", "sf", "sin",
|
|
|
// "fra", etc.
|
|
|
func (v DERPRegionView) RegionCode() string { return v.ж.RegionCode }
|
|
|
|
|
|
// RegionName is a long English name for the region: "New York City",
|
|
|
// "San Francisco", "Singapore", "Frankfurt", etc.
|
|
|
func (v DERPRegionView) RegionName() string { return v.ж.RegionName }
|
|
|
|
|
|
// Latitude, Longitude are optional geographical coordinates of the DERP region's city, in degrees.
|
|
|
func (v DERPRegionView) Latitude() float64 { return v.ж.Latitude }
|
|
|
func (v DERPRegionView) Longitude() float64 { return v.ж.Longitude }
|
|
|
|
|
|
// 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.
|
|
|
func (v DERPRegionView) Avoid() bool { return v.ж.Avoid }
|
|
|
|
|
|
// 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.
|
|
|
func (v DERPRegionView) NoMeasureNoHome() bool { return v.ж.NoMeasureNoHome }
|
|
|
|
|
|
// 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
|
|
|
// (falling back to the second if necessary). STUN packets
|
|
|
// should go to the first 1 or 2.
|
|
|
//
|
|
|
// If nodes within a region route packets amongst themselves,
|
|
|
// but not to other regions. That said, each user/domain
|
|
|
// should get a the same preferred node order, so if all nodes
|
|
|
// for a user/network pick the first one (as they should, when
|
|
|
// things are healthy), the inter-cluster routing is minimal
|
|
|
// to zero.
|
|
|
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
|
|
|
NoMeasureNoHome bool
|
|
|
Nodes []*DERPNode
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of DERPMap.
|
|
|
func (p *DERPMap) View() DERPMapView {
|
|
|
return DERPMapView{ж: p}
|
|
|
}
|
|
|
|
|
|
// DERPMapView provides a read-only view over DERPMap.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type DERPMapView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *DERPMap
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v DERPMapView) AsStruct() *DERPMap {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v DERPMapView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v DERPMapView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *DERPMapView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x DERPMap
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *DERPMapView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x DERPMap
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// HomeParams, if non-nil, is a change in home parameters.
|
|
|
//
|
|
|
// The rest of the DEPRMap fields, if zero, means unchanged.
|
|
|
func (v DERPMapView) HomeParams() DERPHomeParamsView { return v.ж.HomeParams.View() }
|
|
|
|
|
|
// Regions is the set of geographic regions running DERP node(s).
|
|
|
//
|
|
|
// It's keyed by the DERPRegion.RegionID.
|
|
|
//
|
|
|
// The numbers are not necessarily contiguous.
|
|
|
func (v DERPMapView) Regions() views.MapFn[int, *DERPRegion, DERPRegionView] {
|
|
|
return views.MapFnOf(v.ж.Regions, func(t *DERPRegion) DERPRegionView {
|
|
|
return t.View()
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// OmitDefaultRegions specifies to not use Tailscale's DERP servers, and only use those
|
|
|
// specified in this DERPMap. If there are none set outside of the defaults, this is a noop.
|
|
|
//
|
|
|
// This field is only meaningful if the Regions map is non-nil (indicating a change).
|
|
|
func (v DERPMapView) OmitDefaultRegions() bool { return v.ж.OmitDefaultRegions }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _DERPMapViewNeedsRegeneration = DERPMap(struct {
|
|
|
HomeParams *DERPHomeParams
|
|
|
Regions map[int]*DERPRegion
|
|
|
OmitDefaultRegions bool
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of DERPNode.
|
|
|
func (p *DERPNode) View() DERPNodeView {
|
|
|
return DERPNodeView{ж: p}
|
|
|
}
|
|
|
|
|
|
// DERPNodeView provides a read-only view over DERPNode.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type DERPNodeView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *DERPNode
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v DERPNodeView) AsStruct() *DERPNode {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v DERPNodeView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v DERPNodeView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *DERPNodeView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x DERPNode
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *DERPNodeView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x DERPNode
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// Name is a unique node name (across all regions).
|
|
|
// It is not a host name.
|
|
|
// It's typically of the form "1b", "2a", "3b", etc. (region
|
|
|
// ID + suffix within that region)
|
|
|
func (v DERPNodeView) Name() string { return v.ж.Name }
|
|
|
|
|
|
// RegionID is the RegionID of the DERPRegion that this node
|
|
|
// is running in.
|
|
|
func (v DERPNodeView) RegionID() int { return v.ж.RegionID }
|
|
|
|
|
|
// HostName is the DERP node's hostname.
|
|
|
//
|
|
|
// It is required but need not be unique; multiple nodes may
|
|
|
// have the same HostName but vary in configuration otherwise.
|
|
|
func (v DERPNodeView) HostName() string { return v.ж.HostName }
|
|
|
|
|
|
// CertName optionally specifies the expected TLS cert common
|
|
|
// 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.
|
|
|
func (v DERPNodeView) CertName() string { return v.ж.CertName }
|
|
|
|
|
|
// IPv4 optionally forces an IPv4 address to use, instead of using DNS.
|
|
|
// If empty, A record(s) from DNS lookups of HostName are used.
|
|
|
// If the string is not an IPv4 address, IPv4 is not used; the
|
|
|
// conventional string to disable IPv4 (and not use DNS) is
|
|
|
// "none".
|
|
|
func (v DERPNodeView) IPv4() string { return v.ж.IPv4 }
|
|
|
|
|
|
// IPv6 optionally forces an IPv6 address to use, instead of using DNS.
|
|
|
// If empty, AAAA record(s) from DNS lookups of HostName are used.
|
|
|
// If the string is not an IPv6 address, IPv6 is not used; the
|
|
|
// conventional string to disable IPv6 (and not use DNS) is
|
|
|
// "none".
|
|
|
func (v DERPNodeView) IPv6() string { return v.ж.IPv6 }
|
|
|
|
|
|
// Port optionally specifies a STUN port to use.
|
|
|
// Zero means 3478.
|
|
|
// To disable STUN on this node, use -1.
|
|
|
func (v DERPNodeView) STUNPort() int { return v.ж.STUNPort }
|
|
|
|
|
|
// STUNOnly marks a node as only a STUN server and not a DERP
|
|
|
// server.
|
|
|
func (v DERPNodeView) STUNOnly() bool { return v.ж.STUNOnly }
|
|
|
|
|
|
// DERPPort optionally provides an alternate TLS port number
|
|
|
// for the DERP HTTPS server.
|
|
|
//
|
|
|
// If zero, 443 is used.
|
|
|
func (v DERPNodeView) DERPPort() int { return v.ж.DERPPort }
|
|
|
|
|
|
// InsecureForTests is used by unit tests to disable TLS verification.
|
|
|
// It should not be set by users.
|
|
|
func (v DERPNodeView) InsecureForTests() bool { return v.ж.InsecureForTests }
|
|
|
|
|
|
// STUNTestIP is used in tests to override the STUN server's IP.
|
|
|
// If empty, it's assumed to be the same as the DERP server.
|
|
|
func (v DERPNodeView) STUNTestIP() string { return v.ж.STUNTestIP }
|
|
|
|
|
|
// CanPort80 specifies whether this DERP node is accessible over HTTP
|
|
|
// on port 80 specifically. This is used for captive portal checks.
|
|
|
func (v DERPNodeView) CanPort80() bool { return v.ж.CanPort80 }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _DERPNodeViewNeedsRegeneration = DERPNode(struct {
|
|
|
Name string
|
|
|
RegionID int
|
|
|
HostName string
|
|
|
CertName string
|
|
|
IPv4 string
|
|
|
IPv6 string
|
|
|
STUNPort int
|
|
|
STUNOnly bool
|
|
|
DERPPort int
|
|
|
InsecureForTests bool
|
|
|
STUNTestIP string
|
|
|
CanPort80 bool
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of SSHRule.
|
|
|
func (p *SSHRule) View() SSHRuleView {
|
|
|
return SSHRuleView{ж: p}
|
|
|
}
|
|
|
|
|
|
// SSHRuleView provides a read-only view over SSHRule.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type SSHRuleView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *SSHRule
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v SSHRuleView) AsStruct() *SSHRule {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v SSHRuleView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v SSHRuleView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *SSHRuleView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x SSHRule
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *SSHRuleView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x SSHRule
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// RuleExpires, if non-nil, is when this rule expires.
|
|
|
//
|
|
|
// For example, a (principal,sshuser) tuple might be granted
|
|
|
// prompt-free SSH access for N minutes, so this rule would be
|
|
|
// before a expiration-free rule for the same principal that
|
|
|
// required an auth prompt. This permits the control plane to
|
|
|
// be out of the path for already-authorized SSH pairs.
|
|
|
//
|
|
|
// Once a rule matches, the lifetime of any accepting connection
|
|
|
// is subject to the SSHAction.SessionExpires time, if any.
|
|
|
func (v SSHRuleView) RuleExpires() views.ValuePointer[time.Time] {
|
|
|
return views.ValuePointerOf(v.ж.RuleExpires)
|
|
|
}
|
|
|
|
|
|
// Principals matches an incoming connection. If the connection
|
|
|
// matches anything in this list and also matches SSHUsers,
|
|
|
// then Action is applied.
|
|
|
func (v SSHRuleView) Principals() views.SliceView[*SSHPrincipal, SSHPrincipalView] {
|
|
|
return views.SliceOfViews[*SSHPrincipal, SSHPrincipalView](v.ж.Principals)
|
|
|
}
|
|
|
|
|
|
// SSHUsers are the SSH users that this rule matches. It is a
|
|
|
// map from either ssh-user|"*" => local-user. The map must
|
|
|
// contain a key for either ssh-user or, as a fallback, "*" to
|
|
|
// match anything. If it does, the map entry's value is the
|
|
|
// actual user that's logged in.
|
|
|
// If the map value is the empty string (for either the
|
|
|
// requested SSH user or "*"), the rule doesn't match.
|
|
|
// If the map value is "=", it means the ssh-user should map
|
|
|
// directly to the local-user.
|
|
|
// It may be nil if the Action is reject.
|
|
|
func (v SSHRuleView) SSHUsers() views.Map[string, string] { return views.MapOf(v.ж.SSHUsers) }
|
|
|
|
|
|
// Action is the outcome to task.
|
|
|
// A nil or invalid action means to deny.
|
|
|
func (v SSHRuleView) Action() SSHActionView { return v.ж.Action.View() }
|
|
|
|
|
|
// AcceptEnv is a slice of environment variable names that are allowlisted
|
|
|
// for the SSH rule in the policy file.
|
|
|
//
|
|
|
// AcceptEnv values may contain * and ? wildcard characters which match against
|
|
|
// an arbitrary number of characters or a single character respectively.
|
|
|
func (v SSHRuleView) AcceptEnv() views.Slice[string] { return views.SliceOf(v.ж.AcceptEnv) }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _SSHRuleViewNeedsRegeneration = SSHRule(struct {
|
|
|
RuleExpires *time.Time
|
|
|
Principals []*SSHPrincipal
|
|
|
SSHUsers map[string]string
|
|
|
Action *SSHAction
|
|
|
AcceptEnv []string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of SSHAction.
|
|
|
func (p *SSHAction) View() SSHActionView {
|
|
|
return SSHActionView{ж: p}
|
|
|
}
|
|
|
|
|
|
// SSHActionView provides a read-only view over SSHAction.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type SSHActionView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *SSHAction
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v SSHActionView) AsStruct() *SSHAction {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v SSHActionView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v SSHActionView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *SSHActionView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x SSHAction
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *SSHActionView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x SSHAction
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// Message, if non-empty, is shown to the user before the
|
|
|
// action occurs.
|
|
|
func (v SSHActionView) Message() string { return v.ж.Message }
|
|
|
|
|
|
// Reject, if true, terminates the connection. This action
|
|
|
// has higher priority that Accept, if given.
|
|
|
// The reason this is exists is primarily so a response
|
|
|
// from HoldAndDelegate has a way to stop the poll.
|
|
|
func (v SSHActionView) Reject() bool { return v.ж.Reject }
|
|
|
|
|
|
// Accept, if true, accepts the connection immediately
|
|
|
// without further prompts.
|
|
|
func (v SSHActionView) Accept() bool { return v.ж.Accept }
|
|
|
|
|
|
// SessionDuration, if non-zero, is how long the session can stay open
|
|
|
// before being forcefully terminated.
|
|
|
func (v SSHActionView) SessionDuration() time.Duration { return v.ж.SessionDuration }
|
|
|
|
|
|
// AllowAgentForwarding, if true, allows accepted connections to forward
|
|
|
// the ssh agent if requested.
|
|
|
func (v SSHActionView) AllowAgentForwarding() bool { return v.ж.AllowAgentForwarding }
|
|
|
|
|
|
// HoldAndDelegate, if non-empty, is a URL that serves an
|
|
|
// outcome verdict. The connection will be accepted and will
|
|
|
// block until the provided long-polling URL serves a new
|
|
|
// SSHAction JSON value. The URL must be fetched using the
|
|
|
// Noise transport (in package control/control{base,http}).
|
|
|
// If the long poll breaks before returning a complete HTTP
|
|
|
// response, it should be re-fetched as long as the SSH
|
|
|
// session is open.
|
|
|
//
|
|
|
// The following variables in the URL are expanded by tailscaled:
|
|
|
//
|
|
|
// - $SRC_NODE_IP (URL escaped)
|
|
|
// - $SRC_NODE_ID (Node.ID as int64 string)
|
|
|
// - $DST_NODE_IP (URL escaped)
|
|
|
// - $DST_NODE_ID (Node.ID as int64 string)
|
|
|
// - $SSH_USER (URL escaped, ssh user requested)
|
|
|
// - $LOCAL_USER (URL escaped, local user mapped)
|
|
|
func (v SSHActionView) HoldAndDelegate() string { return v.ж.HoldAndDelegate }
|
|
|
|
|
|
// AllowLocalPortForwarding, if true, allows accepted connections
|
|
|
// to use local port forwarding if requested.
|
|
|
func (v SSHActionView) AllowLocalPortForwarding() bool { return v.ж.AllowLocalPortForwarding }
|
|
|
|
|
|
// AllowRemotePortForwarding, if true, allows accepted connections
|
|
|
// to use remote port forwarding if requested.
|
|
|
func (v SSHActionView) AllowRemotePortForwarding() bool { return v.ж.AllowRemotePortForwarding }
|
|
|
|
|
|
// Recorders defines the destinations of the SSH session recorders.
|
|
|
// The recording will be uploaded to http://addr:port/record.
|
|
|
func (v SSHActionView) Recorders() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Recorders) }
|
|
|
|
|
|
// OnRecorderFailure is the action to take if recording fails.
|
|
|
// If nil, the default action is to fail open.
|
|
|
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.
|
|
|
var _SSHActionViewNeedsRegeneration = SSHAction(struct {
|
|
|
Message string
|
|
|
Reject bool
|
|
|
Accept bool
|
|
|
SessionDuration time.Duration
|
|
|
AllowAgentForwarding bool
|
|
|
HoldAndDelegate string
|
|
|
AllowLocalPortForwarding bool
|
|
|
AllowRemotePortForwarding bool
|
|
|
Recorders []netip.AddrPort
|
|
|
OnRecordingFailure *SSHRecorderFailureAction
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of SSHPrincipal.
|
|
|
func (p *SSHPrincipal) View() SSHPrincipalView {
|
|
|
return SSHPrincipalView{ж: p}
|
|
|
}
|
|
|
|
|
|
// SSHPrincipalView provides a read-only view over SSHPrincipal.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type SSHPrincipalView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *SSHPrincipal
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v SSHPrincipalView) AsStruct() *SSHPrincipal {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v SSHPrincipalView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v SSHPrincipalView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x SSHPrincipal
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *SSHPrincipalView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x SSHPrincipal
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node }
|
|
|
func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP }
|
|
|
|
|
|
// email-ish: foo@example.com, bar@github
|
|
|
func (v SSHPrincipalView) UserLogin() string { return v.ж.UserLogin }
|
|
|
|
|
|
// if true, match any connection
|
|
|
func (v SSHPrincipalView) Any() bool { return v.ж.Any }
|
|
|
|
|
|
// 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.
|
|
|
//
|
|
|
// Deprecated: do not use. It does nothing.
|
|
|
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
|
|
|
UnusedPubKeys []string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of ControlDialPlan.
|
|
|
func (p *ControlDialPlan) View() ControlDialPlanView {
|
|
|
return ControlDialPlanView{ж: p}
|
|
|
}
|
|
|
|
|
|
// ControlDialPlanView provides a read-only view over ControlDialPlan.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type ControlDialPlanView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *ControlDialPlan
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v ControlDialPlanView) AsStruct() *ControlDialPlan {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v ControlDialPlanView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v ControlDialPlanView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *ControlDialPlanView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x ControlDialPlan
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *ControlDialPlanView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x ControlDialPlan
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// An empty list means the default: use DNS (unspecified which DNS).
|
|
|
func (v ControlDialPlanView) Candidates() views.Slice[ControlIPCandidate] {
|
|
|
return views.SliceOf(v.ж.Candidates)
|
|
|
}
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _ControlDialPlanViewNeedsRegeneration = ControlDialPlan(struct {
|
|
|
Candidates []ControlIPCandidate
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of Location.
|
|
|
func (p *Location) View() LocationView {
|
|
|
return LocationView{ж: p}
|
|
|
}
|
|
|
|
|
|
// LocationView provides a read-only view over Location.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type LocationView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *Location
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v LocationView) AsStruct() *Location {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v LocationView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v LocationView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *LocationView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x Location
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *LocationView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x Location
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// User friendly country name, with proper capitalization ("Canada")
|
|
|
func (v LocationView) Country() string { return v.ж.Country }
|
|
|
|
|
|
// ISO 3166-1 alpha-2 in upper case ("CA")
|
|
|
func (v LocationView) CountryCode() string { return v.ж.CountryCode }
|
|
|
|
|
|
// User friendly city name, with proper capitalization ("Squamish")
|
|
|
func (v LocationView) City() string { return v.ж.City }
|
|
|
|
|
|
// CityCode is a short code representing the city in upper case.
|
|
|
// CityCode is used to disambiguate a city from another location
|
|
|
// with the same city name. It uniquely identifies a particular
|
|
|
// geographical location, within the tailnet.
|
|
|
// IATA, ICAO or ISO 3166-2 codes are recommended ("YSE")
|
|
|
func (v LocationView) CityCode() string { return v.ж.CityCode }
|
|
|
|
|
|
// Latitude, Longitude are optional geographical coordinates of the node, in degrees.
|
|
|
// No particular accuracy level is promised; the coordinates may simply be the center of the city or country.
|
|
|
func (v LocationView) Latitude() float64 { return v.ж.Latitude }
|
|
|
func (v LocationView) Longitude() float64 { return v.ж.Longitude }
|
|
|
|
|
|
// Priority determines the order of use of an exit node when a
|
|
|
// location based preference matches more than one exit node,
|
|
|
// the node with the highest priority wins. Nodes of equal
|
|
|
// probability may be selected arbitrarily.
|
|
|
//
|
|
|
// A value of 0 means the exit node does not have a priority
|
|
|
// preference. A negative int is not allowed.
|
|
|
func (v LocationView) Priority() int { return v.ж.Priority }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _LocationViewNeedsRegeneration = Location(struct {
|
|
|
Country string
|
|
|
CountryCode string
|
|
|
City string
|
|
|
CityCode string
|
|
|
Latitude float64
|
|
|
Longitude float64
|
|
|
Priority int
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of UserProfile.
|
|
|
func (p *UserProfile) View() UserProfileView {
|
|
|
return UserProfileView{ж: p}
|
|
|
}
|
|
|
|
|
|
// UserProfileView provides a read-only view over UserProfile.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type UserProfileView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *UserProfile
|
|
|
}
|
|
|
|
|
|
// 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
|
|
|
// the original.
|
|
|
func (v UserProfileView) AsStruct() *UserProfile {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v UserProfileView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v UserProfileView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *UserProfileView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x UserProfile
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *UserProfileView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x UserProfile
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (v UserProfileView) ID() UserID { return v.ж.ID }
|
|
|
|
|
|
// "alice@smith.com"; for display purposes only (provider is not listed)
|
|
|
func (v UserProfileView) LoginName() string { return v.ж.LoginName }
|
|
|
|
|
|
// "Alice Smith"
|
|
|
func (v UserProfileView) DisplayName() string { return v.ж.DisplayName }
|
|
|
func (v UserProfileView) ProfilePicURL() string { return v.ж.ProfilePicURL }
|
|
|
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.
|
|
|
var _UserProfileViewNeedsRegeneration = UserProfile(struct {
|
|
|
ID UserID
|
|
|
LoginName string
|
|
|
DisplayName string
|
|
|
ProfilePicURL string
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of VIPService.
|
|
|
func (p *VIPService) View() VIPServiceView {
|
|
|
return VIPServiceView{ж: p}
|
|
|
}
|
|
|
|
|
|
// VIPServiceView provides a read-only view over VIPService.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type VIPServiceView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *VIPService
|
|
|
}
|
|
|
|
|
|
// Valid reports whether v's underlying value is non-nil.
|
|
|
func (v VIPServiceView) Valid() bool { return v.ж != nil }
|
|
|
|
|
|
// AsStruct returns a clone of the underlying value which aliases no memory with
|
|
|
// the original.
|
|
|
func (v VIPServiceView) AsStruct() *VIPService {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v VIPServiceView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v VIPServiceView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *VIPServiceView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x VIPService
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *VIPServiceView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x VIPService
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// 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.
|
|
|
func (v VIPServiceView) Name() ServiceName { return v.ж.Name }
|
|
|
|
|
|
// Ports specify which ProtoPorts are made available by this node
|
|
|
// on the service's IPs.
|
|
|
func (v VIPServiceView) Ports() views.Slice[ProtoPortRange] { return views.SliceOf(v.ж.Ports) }
|
|
|
|
|
|
// Active specifies whether new requests for the service should be
|
|
|
// sent to this node by control.
|
|
|
func (v VIPServiceView) Active() bool { return v.ж.Active }
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _VIPServiceViewNeedsRegeneration = VIPService(struct {
|
|
|
Name ServiceName
|
|
|
Ports []ProtoPortRange
|
|
|
Active bool
|
|
|
}{})
|
|
|
|
|
|
// View returns a read-only view of SSHPolicy.
|
|
|
func (p *SSHPolicy) View() SSHPolicyView {
|
|
|
return SSHPolicyView{ж: p}
|
|
|
}
|
|
|
|
|
|
// SSHPolicyView provides a read-only view over SSHPolicy.
|
|
|
//
|
|
|
// Its methods should only be called if `Valid()` returns true.
|
|
|
type SSHPolicyView struct {
|
|
|
// ж is the underlying mutable value, named with a hard-to-type
|
|
|
// character that looks pointy like a pointer.
|
|
|
// It is named distinctively to make you think of how dangerous it is to escape
|
|
|
// to callers. You must not let callers be able to mutate it.
|
|
|
ж *SSHPolicy
|
|
|
}
|
|
|
|
|
|
// Valid reports whether v's underlying value is non-nil.
|
|
|
func (v SSHPolicyView) Valid() bool { return v.ж != nil }
|
|
|
|
|
|
// AsStruct returns a clone of the underlying value which aliases no memory with
|
|
|
// the original.
|
|
|
func (v SSHPolicyView) AsStruct() *SSHPolicy {
|
|
|
if v.ж == nil {
|
|
|
return nil
|
|
|
}
|
|
|
return v.ж.Clone()
|
|
|
}
|
|
|
|
|
|
// MarshalJSON implements [jsonv1.Marshaler].
|
|
|
func (v SSHPolicyView) MarshalJSON() ([]byte, error) {
|
|
|
return jsonv1.Marshal(v.ж)
|
|
|
}
|
|
|
|
|
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
|
|
func (v SSHPolicyView) MarshalJSONTo(enc *jsontext.Encoder) error {
|
|
|
return jsonv2.MarshalEncode(enc, v.ж)
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSON implements [jsonv1.Unmarshaler].
|
|
|
func (v *SSHPolicyView) UnmarshalJSON(b []byte) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
if len(b) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
var x SSHPolicy
|
|
|
if err := jsonv1.Unmarshal(b, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
|
|
func (v *SSHPolicyView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
|
|
if v.ж != nil {
|
|
|
return errors.New("already initialized")
|
|
|
}
|
|
|
var x SSHPolicy
|
|
|
if err := jsonv2.UnmarshalDecode(dec, &x); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
v.ж = &x
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// Rules are the rules to process for an incoming SSH connection. The first
|
|
|
// matching rule takes its action and stops processing further rules.
|
|
|
//
|
|
|
// When an incoming connection first starts, all rules are evaluated in
|
|
|
// "none" auth mode, where the client hasn't even been asked to send a
|
|
|
// public key. All SSHRule.Principals requiring a public key won't match. If
|
|
|
// a rule matches on the first pass and its Action is reject, the
|
|
|
// authentication fails with that action's rejection message, if any.
|
|
|
//
|
|
|
// If the first pass rule evaluation matches nothing without matching an
|
|
|
// Action with Reject set, the rules are considered to see whether public
|
|
|
// keys might still result in a match. If not, "none" auth is terminated
|
|
|
// before proceeding to public key mode. If so, the client is asked to try
|
|
|
// public key authentication and the rules are evaluated again for each of
|
|
|
// the client's present keys.
|
|
|
func (v SSHPolicyView) Rules() views.SliceView[*SSHRule, SSHRuleView] {
|
|
|
return views.SliceOfViews[*SSHRule, SSHRuleView](v.ж.Rules)
|
|
|
}
|
|
|
|
|
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
|
|
var _SSHPolicyViewNeedsRegeneration = SSHPolicy(struct {
|
|
|
Rules []*SSHRule
|
|
|
}{})
|