ssh/tailssh: add nodeCap SSH principal

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
knyar/sshcap
Anton Tolchanov 3 months ago
parent 4657cbdb11
commit 99f0c03e9b

@ -1201,6 +1201,9 @@ func (c *conn) principalMatchesTailscaleIdentity(p *tailcfg.SSHPrincipal) bool {
return true
}
}
if p.NodeCap != "" && ci.node.Valid() && ci.node.HasCap(p.NodeCap) {
return true
}
if p.UserLogin != "" && ci.uprof.LoginName == p.UserLogin {
return true
}

@ -202,6 +202,16 @@ func TestMatchRule(t *testing.T) {
ci: &sshConnInfo{node: (&tailcfg.Node{StableID: "some-node-ID"}).View()},
wantUser: "ubuntu",
},
{
name: "match-principal-node-cap",
rule: &tailcfg.SSHRule{
Action: someAction,
Principals: []*tailcfg.SSHPrincipal{{NodeCap: "some-node-cap"}},
SSHUsers: map[string]string{"*": "ubuntu"},
},
ci: &sshConnInfo{node: (&tailcfg.Node{CapMap: tailcfg.NodeCapMap{"some-node-cap": nil}}).View()},
wantUser: "ubuntu",
},
{
name: "match-principal-userlogin",
rule: &tailcfg.SSHRule{

@ -2841,13 +2841,14 @@ type SSHRule struct {
// SSHPrincipal is either a particular node or a user on any node.
type SSHPrincipal struct {
// Matching any one of the following four field causes a match.
// Matching any one of the following five field causes a match.
// It must also match Certs, if non-empty.
Node StableNodeID `json:"node,omitempty"`
NodeIP string `json:"nodeIP,omitempty"`
UserLogin string `json:"userLogin,omitempty"` // email-ish: foo@example.com, bar@github
Any bool `json:"any,omitempty"` // if true, match any connection
Node StableNodeID `json:"node,omitempty"`
NodeIP string `json:"nodeIP,omitempty"`
NodeCap NodeCapability `json:"nodeCap,omitempty"`
UserLogin string `json:"userLogin,omitempty"` // email-ish: foo@example.com, bar@github
Any bool `json:"any,omitempty"` // if true, match any connection
// TODO(bradfitz): add StableUserID, once that exists
// UnusedPubKeys was public key support. It never became an official product

@ -569,6 +569,7 @@ func (src *SSHPrincipal) Clone() *SSHPrincipal {
var _SSHPrincipalCloneNeedsRegeneration = SSHPrincipal(struct {
Node StableNodeID
NodeIP string
NodeCap NodeCapability
UserLogin string
Any bool
UnusedPubKeys []string

@ -2220,8 +2220,9 @@ func (v *SSHPrincipalView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
return nil
}
func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node }
func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP }
func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node }
func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP }
func (v SSHPrincipalView) NodeCap() NodeCapability { return v.ж.NodeCap }
// email-ish: foo@example.com, bar@github
func (v SSHPrincipalView) UserLogin() string { return v.ж.UserLogin }
@ -2243,6 +2244,7 @@ func (v SSHPrincipalView) UnusedPubKeys() views.Slice[string] {
var _SSHPrincipalViewNeedsRegeneration = SSHPrincipal(struct {
Node StableNodeID
NodeIP string
NodeCap NodeCapability
UserLogin string
Any bool
UnusedPubKeys []string

Loading…
Cancel
Save