mirror of https://github.com/tailscale/tailscale/
ssh/tailssh: evaluate tailcfg.SSHPolicy on incoming connections
Updates #3802 Fixes #3960 Change-Id: Ieda2007d462ddce6c217b958167417ae9755774e Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/3981/head
parent
66f5aa6814
commit
e1e20f6d39
@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package tailssh
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func TestMatchRule(t *testing.T) {
|
||||
someAction := new(tailcfg.SSHAction)
|
||||
tests := []struct {
|
||||
name string
|
||||
rule *tailcfg.SSHRule
|
||||
ctx *sshContext
|
||||
wantErr error
|
||||
wantUser string
|
||||
}{
|
||||
{
|
||||
name: "nil-rule",
|
||||
rule: nil,
|
||||
wantErr: errNilRule,
|
||||
},
|
||||
{
|
||||
name: "nil-action",
|
||||
rule: &tailcfg.SSHRule{},
|
||||
wantErr: errNilAction,
|
||||
},
|
||||
{
|
||||
name: "expired",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
RuleExpires: timePtr(time.Unix(100, 0)),
|
||||
},
|
||||
ctx: &sshContext{now: time.Unix(200, 0)},
|
||||
wantErr: errRuleExpired,
|
||||
},
|
||||
{
|
||||
name: "no-principal",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
},
|
||||
wantErr: errPrincipalMatch,
|
||||
},
|
||||
{
|
||||
name: "no-user-match",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{{Any: true}},
|
||||
},
|
||||
ctx: &sshContext{sshUser: "alice"},
|
||||
wantErr: errUserMatch,
|
||||
},
|
||||
{
|
||||
name: "ok-wildcard",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{{Any: true}},
|
||||
SSHUsers: map[string]string{
|
||||
"*": "ubuntu",
|
||||
},
|
||||
},
|
||||
ctx: &sshContext{sshUser: "alice"},
|
||||
wantUser: "ubuntu",
|
||||
},
|
||||
{
|
||||
name: "ok-wildcard-and-nil-principal",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{
|
||||
nil, // don't crash on this
|
||||
{Any: true},
|
||||
},
|
||||
SSHUsers: map[string]string{
|
||||
"*": "ubuntu",
|
||||
},
|
||||
},
|
||||
ctx: &sshContext{sshUser: "alice"},
|
||||
wantUser: "ubuntu",
|
||||
},
|
||||
{
|
||||
name: "ok-exact",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{{Any: true}},
|
||||
SSHUsers: map[string]string{
|
||||
"*": "ubuntu",
|
||||
"alice": "thealice",
|
||||
},
|
||||
},
|
||||
ctx: &sshContext{sshUser: "alice"},
|
||||
wantUser: "thealice",
|
||||
},
|
||||
{
|
||||
name: "no-users-for-reject",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Principals: []*tailcfg.SSHPrincipal{{Any: true}},
|
||||
Action: &tailcfg.SSHAction{Reject: true},
|
||||
},
|
||||
ctx: &sshContext{sshUser: "alice"},
|
||||
},
|
||||
{
|
||||
name: "match-principal-node-ip",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{{NodeIP: "1.2.3.4"}},
|
||||
SSHUsers: map[string]string{"*": "ubuntu"},
|
||||
},
|
||||
ctx: &sshContext{srcIP: netaddr.MustParseIP("1.2.3.4")},
|
||||
wantUser: "ubuntu",
|
||||
},
|
||||
{
|
||||
name: "match-principal-node-id",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{{Node: "some-node-ID"}},
|
||||
SSHUsers: map[string]string{"*": "ubuntu"},
|
||||
},
|
||||
ctx: &sshContext{node: &tailcfg.Node{StableID: "some-node-ID"}},
|
||||
wantUser: "ubuntu",
|
||||
},
|
||||
{
|
||||
name: "match-principal-userlogin",
|
||||
rule: &tailcfg.SSHRule{
|
||||
Action: someAction,
|
||||
Principals: []*tailcfg.SSHPrincipal{{UserLogin: "foo@bar.com"}},
|
||||
SSHUsers: map[string]string{"*": "ubuntu"},
|
||||
},
|
||||
ctx: &sshContext{uprof: &tailcfg.UserProfile{LoginName: "foo@bar.com"}},
|
||||
wantUser: "ubuntu",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, gotUser, err := matchRule(tt.rule, tt.ctx)
|
||||
if err != tt.wantErr {
|
||||
t.Errorf("err = %v; want %v", err, tt.wantErr)
|
||||
}
|
||||
if gotUser != tt.wantUser {
|
||||
t.Errorf("user = %q; want %q", gotUser, tt.wantUser)
|
||||
}
|
||||
if err == nil && got == nil {
|
||||
t.Errorf("expected non-nil action on success")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func timePtr(t time.Time) *time.Time { return &t }
|
Loading…
Reference in New Issue