MM-30319: Fix race in ensureAsymmetricSigningKey (#16354)

A cluster changed listener always runs in a separate goroutine.
This causes a race condition when a new member gets added in the cluster.

We fix it by accessing the value atomically.

I tried to write a test for this, but it wasn't triggering at all.
The best way is to just start the server with cluster enabled in race mode,
and verify that the race doesn't happen.

https://mattermost.atlassian.net/browse/MM-30319

```release-notes
NONE
```
Этот коммит содержится в:
Agniva De Sarker
2020-11-23 19:18:24 +05:30
коммит произвёл GitHub
родитель d28e7ec870
Коммит d9e8402dc2
2 изменённых файлов: 11 добавлений и 9 удалений

Просмотреть файл

@@ -164,7 +164,7 @@ func (s *Server) ensurePostActionCookieSecret() error {
// ensureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to // ensureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to
// AsymmetricSigningKey will always return a valid signing key. // AsymmetricSigningKey will always return a valid signing key.
func (s *Server) ensureAsymmetricSigningKey() error { func (s *Server) ensureAsymmetricSigningKey() error {
if s.asymmetricSigningKey != nil { if s.AsymmetricSigningKey() != nil {
return nil return nil
} }
@@ -227,14 +227,14 @@ func (s *Server) ensureAsymmetricSigningKey() error {
default: default:
return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve) return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve)
} }
s.asymmetricSigningKey = &ecdsa.PrivateKey{ s.asymmetricSigningKey.Store(&ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{ PublicKey: ecdsa.PublicKey{
Curve: curve, Curve: curve,
X: key.ECDSAKey.X, X: key.ECDSAKey.X,
Y: key.ECDSAKey.Y, Y: key.ECDSAKey.Y,
}, },
D: key.ECDSAKey.D, D: key.ECDSAKey.D,
} })
s.regenerateClientConfig() s.regenerateClientConfig()
return nil return nil
} }
@@ -279,7 +279,10 @@ func (s *Server) ensureFirstServerRunTimestamp() error {
// AsymmetricSigningKey will return a private key that can be used for asymmetric signing. // AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey { func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey {
return s.asymmetricSigningKey if key := s.asymmetricSigningKey.Load(); key != nil {
return key.(*ecdsa.PrivateKey)
}
return nil
} }
func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey { func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {

Просмотреть файл

@@ -5,7 +5,6 @@ package app
import ( import (
"context" "context"
"crypto/ecdsa"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"hash/maphash" "hash/maphash"
@@ -129,7 +128,6 @@ type Server struct {
searchLicenseListenerId string searchLicenseListenerId string
loggerLicenseListenerId string loggerLicenseListenerId string
configStore *config.Store configStore *config.Store
asymmetricSigningKey *ecdsa.PrivateKey
postActionCookieSecret []byte postActionCookieSecret []byte
advancedLogListenerCleanup func() advancedLogListenerCleanup func()
@@ -137,9 +135,10 @@ type Server struct {
pluginCommands []*PluginCommand pluginCommands []*PluginCommand
pluginCommandsLock sync.RWMutex pluginCommandsLock sync.RWMutex
clientConfig atomic.Value asymmetricSigningKey atomic.Value
clientConfigHash atomic.Value clientConfig atomic.Value
limitedClientConfig atomic.Value clientConfigHash atomic.Value
limitedClientConfig atomic.Value
telemetryService *telemetry.TelemetryService telemetryService *telemetry.TelemetryService