nomad: only activate one-time auth tokens with 1.1.0 (#10952)

Fix a panic in handling one-time auth tokens, used to support `nomad ui
--authenticate`.

If the nomad leader is a 1.1.x with some servers running as 1.0.x, the
pre-1.1.0 servers risk crashing and the cluster may lose quorum. That
can happen when `nomad authenticate -ui` command is issued, or when the
leader scans for expired tokens every 10 minutes.

Fixed #10943 .
This commit is contained in:
Mahmood Ali 2021-07-27 13:17:55 -04:00 committed by GitHub
parent a15d03556c
commit ac3cf10849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 21 additions and 0 deletions

3
.changelog/10952.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
core: Fixed a panic that may arise when upgrading pre-1.1.0 cluster to 1.1.x and may cause cluster outage
```

View File

@ -851,6 +851,10 @@ func (a *ACL) UpsertOneTimeToken(args *structs.OneTimeTokenUpsertRequest, reply
defer metrics.MeasureSince( defer metrics.MeasureSince(
[]string{"nomad", "acl", "upsert_one_time_token"}, time.Now()) []string{"nomad", "acl", "upsert_one_time_token"}, time.Now())
if !ServersMeetMinimumVersion(a.srv.Members(), minOneTimeAuthenticationTokenVersion, false) {
return fmt.Errorf("All servers should be running version %v or later to use one-time authentication tokens", minAutopilotVersion)
}
// Snapshot the state // Snapshot the state
state, err := a.srv.State().Snapshot() state, err := a.srv.State().Snapshot()
if err != nil { if err != nil {
@ -899,6 +903,10 @@ func (a *ACL) ExchangeOneTimeToken(args *structs.OneTimeTokenExchangeRequest, re
defer metrics.MeasureSince( defer metrics.MeasureSince(
[]string{"nomad", "acl", "exchange_one_time_token"}, time.Now()) []string{"nomad", "acl", "exchange_one_time_token"}, time.Now())
if !ServersMeetMinimumVersion(a.srv.Members(), minOneTimeAuthenticationTokenVersion, false) {
return fmt.Errorf("All servers should be running version %v or later to use one-time authentication tokens", minAutopilotVersion)
}
// Snapshot the state // Snapshot the state
state, err := a.srv.State().Snapshot() state, err := a.srv.State().Snapshot()
if err != nil { if err != nil {
@ -952,6 +960,10 @@ func (a *ACL) ExpireOneTimeTokens(args *structs.OneTimeTokenExpireRequest, reply
defer metrics.MeasureSince( defer metrics.MeasureSince(
[]string{"nomad", "acl", "expire_one_time_tokens"}, time.Now()) []string{"nomad", "acl", "expire_one_time_tokens"}, time.Now())
if !ServersMeetMinimumVersion(a.srv.Members(), minOneTimeAuthenticationTokenVersion, false) {
return fmt.Errorf("All servers should be running version %v or later to use one-time authentication tokens", minAutopilotVersion)
}
// Check management level permissions // Check management level permissions
if a.srv.config.ACLEnabled { if a.srv.config.ACLEnabled {
if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil {

View File

@ -48,6 +48,8 @@ var minClusterIDVersion = version.Must(version.NewVersion("0.10.4"))
var minJobRegisterAtomicEvalVersion = version.Must(version.NewVersion("0.12.1")) var minJobRegisterAtomicEvalVersion = version.Must(version.NewVersion("0.12.1"))
var minOneTimeAuthenticationTokenVersion = version.Must(version.NewVersion("1.1.0"))
// monitorLeadership is used to monitor if we acquire or lose our role // monitorLeadership is used to monitor if we acquire or lose our role
// as the leader in the Raft cluster. There is some work the leader is // as the leader in the Raft cluster. There is some work the leader is
// expected to do, so we must react to changes // expected to do, so we must react to changes
@ -739,6 +741,10 @@ func (s *Server) schedulePeriodic(stopCh chan struct{}) {
s.evalBroker.Enqueue(s.coreJobEval(structs.CoreJobCSIVolumeClaimGC, index)) s.evalBroker.Enqueue(s.coreJobEval(structs.CoreJobCSIVolumeClaimGC, index))
} }
case <-oneTimeTokenGC.C: case <-oneTimeTokenGC.C:
if !ServersMeetMinimumVersion(s.Members(), minOneTimeAuthenticationTokenVersion, false) {
continue
}
if index, ok := getLatest(); ok { if index, ok := getLatest(); ok {
s.evalBroker.Enqueue(s.coreJobEval(structs.CoreJobOneTimeTokenGC, index)) s.evalBroker.Enqueue(s.coreJobEval(structs.CoreJobOneTimeTokenGC, index))
} }