@ -281,7 +281,7 @@ func (c *conn) errBanner(message string, err error) error {
if err != nil {
c . logf ( "%s: %s" , message , err )
}
if err := c . spac . SendAuthBanner ( "tailscale: " + message ) ; err != nil {
if err := c . spac . SendAuthBanner ( "tailscale: " + message + "\n" ) ; err != nil {
c . logf ( "failed to send auth banner: %s" , err )
}
return errTerminal
@ -324,9 +324,16 @@ func (c *conn) clientAuth(cm gossh.ConnMetadata) (perms *gossh.Permissions, retE
return nil , c . errBanner ( "failed to get connection info" , err )
}
action , localUser , acceptEnv , err := c . evaluatePolicy ( )
if err != nil {
return nil , c . errBanner ( "failed to evaluate SSH policy" , err )
action , localUser , acceptEnv , result := c . evaluatePolicy ( )
switch result {
case accepted :
// do nothing
case rejectedUser :
return nil , c . errBanner ( fmt . Sprintf ( "tailnet policy does not permit you to SSH as user %q" , c . info . sshUser ) , nil )
case rejected , noPolicy :
return nil , c . errBanner ( "tailnet policy does not permit you to SSH to this node" , fmt . Errorf ( "failed to evaluate policy, result: %s" , result ) )
default :
return nil , c . errBanner ( "failed to evaluate tailnet policy" , fmt . Errorf ( "failed to evaluate policy, result: %s" , result ) )
}
c . action0 = action
@ -597,18 +604,23 @@ func (c *conn) setInfo(cm gossh.ConnMetadata) error {
return nil
}
type evalResult string
const (
noPolicy evalResult = "no policy"
rejected evalResult = "rejected"
rejectedUser evalResult = "rejected user"
accepted evalResult = "accept"
)
// evaluatePolicy returns the SSHAction and localUser after evaluating
// the SSHPolicy for this conn.
func ( c * conn ) evaluatePolicy ( ) ( _ * tailcfg . SSHAction , localUser string , acceptEnv [ ] string , _ error ) {
func ( c * conn ) evaluatePolicy ( ) ( _ * tailcfg . SSHAction , localUser string , acceptEnv [ ] string , result evalResult ) {
pol , ok := c . sshPolicy ( )
if ! ok {
return nil , "" , nil , fmt . Errorf ( "tailssh: rejecting connection; no SSH policy" )
}
a , localUser , acceptEnv , ok := c . evalSSHPolicy ( pol )
if ! ok {
return nil , "" , nil , fmt . Errorf ( "tailssh: rejecting connection; no matching policy" )
return nil , "" , nil , noPolicy
}
return a, localUser , acceptEnv , nil
return c . evalSSHPolicy ( pol )
}
// handleSessionPostSSHAuth runs an SSH session after the SSH-level authentication,
@ -706,9 +718,9 @@ func (c *conn) newSSHSession(s ssh.Session) *sshSession {
// isStillValid reports whether the conn is still valid.
func ( c * conn ) isStillValid ( ) bool {
a , localUser , _ , err := c . evaluatePolicy ( )
c . vlogf ( "stillValid: %+v %v %v" , a , localUser , err )
if err != nil {
a , localUser , _ , result := c . evaluatePolicy ( )
c . vlogf ( "stillValid: %+v %v %v" , a , localUser , result )
if result != accepted {
return false
}
if ! a . Accept && a . HoldAndDelegate == "" {
@ -1089,13 +1101,20 @@ func (c *conn) ruleExpired(r *tailcfg.SSHRule) bool {
return r . RuleExpires . Before ( c . srv . now ( ) )
}
func ( c * conn ) evalSSHPolicy ( pol * tailcfg . SSHPolicy ) ( a * tailcfg . SSHAction , localUser string , acceptEnv [ ] string , ok bool ) {
func ( c * conn ) evalSSHPolicy ( pol * tailcfg . SSHPolicy ) ( a * tailcfg . SSHAction , localUser string , acceptEnv [ ] string , result evalResult ) {
failedOnUser := false
for _ , r := range pol . Rules {
if a , localUser , acceptEnv , err := c . matchRule ( r ) ; err == nil {
return a , localUser , acceptEnv , true
return a , localUser , acceptEnv , accepted
} else if errors . Is ( err , errUserMatch ) {
failedOnUser = true
}
}
return nil , "" , nil , false
result = rejected
if failedOnUser {
result = rejectedUser
}
return nil , "" , nil , result
}
// internal errors for testing; they don't escape to callers or logs.
@ -1129,6 +1148,9 @@ func (c *conn) matchRule(r *tailcfg.SSHRule) (a *tailcfg.SSHAction, localUser st
if c . ruleExpired ( r ) {
return nil , "" , nil , errRuleExpired
}
if ! c . anyPrincipalMatches ( r . Principals ) {
return nil , "" , nil , errPrincipalMatch
}
if ! r . Action . Reject {
// For all but Reject rules, SSHUsers is required.
// If SSHUsers is nil or empty, mapLocalUser will return an
@ -1138,9 +1160,6 @@ func (c *conn) matchRule(r *tailcfg.SSHRule) (a *tailcfg.SSHAction, localUser st
return nil , "" , nil , errUserMatch
}
}
if ! c . anyPrincipalMatches ( r . Principals ) {
return nil , "" , nil , errPrincipalMatch
}
return r . Action , localUser , r . AcceptEnv , nil
}