diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 694c022f0..05891e67b 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -1948,6 +1948,10 @@ 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. diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 3d40a0a18..7545885f7 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -375,6 +375,7 @@ func (src *SSHRule) Clone() *SSHRule { dst.Action = new(SSHAction) *dst.Action = *src.Action } + dst.Recorders = append(src.Recorders[:0:0], src.Recorders...) return dst } @@ -384,6 +385,7 @@ var _SSHRuleCloneNeedsRegeneration = SSHRule(struct { Principals []*SSHPrincipal SSHUsers map[string]string Action *SSHAction + Recorders []netip.AddrPort }{}) // Clone makes a deep copy of SSHPrincipal. diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index a1cc39464..fbede55bb 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -873,12 +873,15 @@ func (v SSHRuleView) Action() *SSHAction { return &x } +func (v SSHRuleView) 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 _SSHRuleViewNeedsRegeneration = SSHRule(struct { RuleExpires *time.Time Principals []*SSHPrincipal SSHUsers map[string]string Action *SSHAction + Recorders []netip.AddrPort }{}) // View returns a readonly view of SSHPrincipal.