From 1b78dc1f33d9cc79a5803289a999f9a50a71ffde Mon Sep 17 00:00:00 2001 From: Charlotte Brandhorst-Satzkorn Date: Tue, 21 Mar 2023 12:59:05 -0700 Subject: [PATCH] tailcfg: move recorders field from SSHRule to SSHAction Signed-off-by: Charlotte Brandhorst-Satzkorn --- tailcfg/tailcfg.go | 12 ++---- tailcfg/tailcfg_clone.go | 42 +++++++++++++++++---- tailcfg/tailcfg_view.go | 79 ++++++++++++++++++++++++++++++++++------ 3 files changed, 107 insertions(+), 26 deletions(-) diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 05891e67b..8e10a8262 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -3,7 +3,7 @@ package tailcfg -//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal,ControlDialPlan --clonefunc +//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan --clonefunc import ( "bytes" @@ -1948,10 +1948,6 @@ type SSHRule struct { // Action is the outcome to task. // A nil or invalid action means to deny. Action *SSHAction `json:"action"` - - // Recorders defines the destinations of the SSH session recorders. - // The recording will be uploaded to http://addr:port/record. - Recorders []netip.AddrPort `json:"recorders"` } // SSHPrincipal is either a particular node or a user on any node. @@ -2025,9 +2021,9 @@ type SSHAction struct { // to use local port forwarding if requested. AllowLocalPortForwarding bool `json:"allowLocalPortForwarding,omitempty"` - // SessionHaulTargetNode, if non-empty, is the Stable ID of a peer to - // stream this SSH session's logs to. - SessionHaulTargetNode StableNodeID `json:"sessionHaulTargetNode,omitempty"` + // Recorders defines the destinations of the SSH session recorders. + // The recording will be uploaded to http://addr:port/record. + Recorders []netip.AddrPort `json:"recorders"` } // OverTLSPublicKeyResponse is the JSON response to /key?v= diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 7545885f7..17557643a 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -371,11 +371,7 @@ func (src *SSHRule) Clone() *SSHRule { dst.SSHUsers[k] = v } } - if dst.Action != nil { - dst.Action = new(SSHAction) - *dst.Action = *src.Action - } - dst.Recorders = append(src.Recorders[:0:0], src.Recorders...) + dst.Action = src.Action.Clone() return dst } @@ -385,7 +381,30 @@ var _SSHRuleCloneNeedsRegeneration = SSHRule(struct { Principals []*SSHPrincipal SSHUsers map[string]string Action *SSHAction - Recorders []netip.AddrPort +}{}) + +// Clone makes a deep copy of SSHAction. +// The result aliases no memory with the original. +func (src *SSHAction) Clone() *SSHAction { + if src == nil { + return nil + } + dst := new(SSHAction) + *dst = *src + dst.Recorders = append(src.Recorders[:0:0], src.Recorders...) + return dst +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHActionCloneNeedsRegeneration = SSHAction(struct { + Message string + Reject bool + Accept bool + SessionDuration time.Duration + AllowAgentForwarding bool + HoldAndDelegate string + AllowLocalPortForwarding bool + Recorders []netip.AddrPort }{}) // Clone makes a deep copy of SSHPrincipal. @@ -428,7 +447,7 @@ var _ControlDialPlanCloneNeedsRegeneration = ControlDialPlan(struct { // Clone duplicates src into dst and reports whether it succeeded. // To succeed, must be of types <*T, *T> or <*T, **T>, -// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal,ControlDialPlan. +// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan. func Clone(dst, src any) bool { switch src := src.(type) { case *User: @@ -530,6 +549,15 @@ func Clone(dst, src any) bool { *dst = src.Clone() return true } + case *SSHAction: + switch dst := dst.(type) { + case *SSHAction: + *dst = *src.Clone() + return true + case **SSHAction: + *dst = src.Clone() + return true + } case *SSHPrincipal: switch dst := dst.(type) { case *SSHPrincipal: diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index fbede55bb..0e8676141 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -20,7 +20,7 @@ import ( "tailscale.com/types/views" ) -//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal,ControlDialPlan +//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan // View returns a readonly view of User. func (p *User) View() UserView { @@ -865,15 +865,7 @@ func (v SSHRuleView) Principals() views.SliceView[*SSHPrincipal, SSHPrincipalVie } func (v SSHRuleView) SSHUsers() views.Map[string, string] { return views.MapOf(v.ж.SSHUsers) } -func (v SSHRuleView) Action() *SSHAction { - if v.ж.Action == nil { - return nil - } - x := *v.ж.Action - return &x -} - -func (v SSHRuleView) Recorders() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Recorders) } +func (v SSHRuleView) Action() SSHActionView { return v.ж.Action.View() } // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _SSHRuleViewNeedsRegeneration = SSHRule(struct { @@ -881,7 +873,72 @@ var _SSHRuleViewNeedsRegeneration = SSHRule(struct { Principals []*SSHPrincipal SSHUsers map[string]string Action *SSHAction - Recorders []netip.AddrPort +}{}) + +// View returns a readonly 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 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() +} + +func (v SSHActionView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +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 := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v SSHActionView) Message() string { return v.ж.Message } +func (v SSHActionView) Reject() bool { return v.ж.Reject } +func (v SSHActionView) Accept() bool { return v.ж.Accept } +func (v SSHActionView) SessionDuration() time.Duration { return v.ж.SessionDuration } +func (v SSHActionView) AllowAgentForwarding() bool { return v.ж.AllowAgentForwarding } +func (v SSHActionView) HoldAndDelegate() string { return v.ж.HoldAndDelegate } +func (v SSHActionView) AllowLocalPortForwarding() bool { return v.ж.AllowLocalPortForwarding } +func (v SSHActionView) Recorders() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Recorders) } + +// 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 + Recorders []netip.AddrPort }{}) // View returns a readonly view of SSHPrincipal.