Files
teleport/lib/auth/native/native.go
T
Jakub Nyckowski 5376f1a9aa Remove test keys (#19507)
A few of our tests that run certificate rotation fail with an error similar to this:

```
can only switch to phase update_clients from init, the current phase is
```

This happens when a duplicated private key is returned and set as a new key during CA rotation. During the CA rotation, we look for duplicated keys https://github.com/gravitational/teleport/blob/962e5a25bc56b5aad05528867182a43cd53cb551/lib/auth/rotate.go#L218 and try to rotate all of them at one. Because some of our tests use still use `testauthority` that only has 4 keys there is a huge chance that the same key will be returned twice, which causes the issue.
I swapped the logic to use a bigger pool with keys introduced in https://github.com/gravitational/teleport/pull/18750. I also removed the randomization part that, after some testing, I discovered was also causing some problems.

We could probably refactor the code in many tests now to make it simpler, but I want to keep this change as simple as possible. Otherwise, backporting bigger changes to older branches takes a long time.
2022-12-21 16:44:04 +00:00

174 lines
4.3 KiB
Go

/*
Copyright 2015 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package native
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"sync"
"testing"
"time"
"github.com/gravitational/trace"
"github.com/sirupsen/logrus"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/keys"
)
var log = logrus.WithFields(logrus.Fields{
trace.Component: teleport.ComponentKeyGen,
})
// precomputedKeys is a queue of cached keys ready for usage.
var precomputedKeys = make(chan *rsa.PrivateKey, 25)
// startPrecomputeOnce is used to start the background task that precomputes key pairs.
var startPrecomputeOnce sync.Once
// GenerateKeyPair generates a new RSA key pair.
func GenerateKeyPair() ([]byte, []byte, error) {
priv, err := GeneratePrivateKey()
if err != nil {
return nil, nil, trace.Wrap(err)
}
return priv.PrivateKeyPEM(), priv.MarshalSSHPublicKey(), nil
}
// GeneratePrivateKey generates a new RSA private key.
func GeneratePrivateKey() (*keys.PrivateKey, error) {
rsaKey, err := getOrGenerateRSAPrivateKey()
if err != nil {
return nil, trace.Wrap(err)
}
// We encode the private key in PKCS #1, ASN.1 DER form
// instead of PKCS #8 to maintain compatibility with some
// third party clients.
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: keys.PKCS1PrivateKeyType,
Headers: nil,
Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
})
return keys.NewPrivateKey(rsaKey, keyPEM)
}
func GenerateRSAPrivateKey() (*rsa.PrivateKey, error) {
return getOrGenerateRSAPrivateKey()
}
func getOrGenerateRSAPrivateKey() (*rsa.PrivateKey, error) {
select {
case k := <-precomputedKeys:
return k, nil
default:
rsaKeyPair, err := generateRSAPrivateKey()
if err != nil {
return nil, err
}
return rsaKeyPair, nil
}
}
func generateRSAPrivateKey() (*rsa.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, constants.RSAKeySize)
}
func precomputeKeys() {
const backoff = time.Second * 30
for {
rsaPrivateKey, err := generateRSAPrivateKey()
if err != nil {
log.WithError(err).Errorf("Failed to precompute key pair, retrying in %s (this might be a bug).", backoff)
time.Sleep(backoff)
}
precomputedKeys <- rsaPrivateKey
}
}
func precomputeTestKeys() {
testKeys, err := generateTestKeys()
if err != nil {
// Use only in tests. Safe to panic.
panic(err)
}
for {
for _, k := range testKeys {
precomputedKeys <- k
}
}
}
// testKeysNumber is the number of RSA keys generated in tests.
const testKeysNumber = 25
func generateTestKeys() ([]*rsa.PrivateKey, error) {
privateKeys := make([]*rsa.PrivateKey, 0, testKeysNumber)
keysChan := make(chan *rsa.PrivateKey)
errC := make(chan error)
go func() {
for i := 0; i < testKeysNumber; i++ {
// Generate each key in a separate goroutine to take advantage of
// multiple cores if possible.
go func() {
private, err := generateRSAPrivateKey()
if err != nil {
errC <- trace.Wrap(err)
return
}
keysChan <- private
}()
}
}()
for i := 0; i < testKeysNumber; i++ {
select {
case err := <-errC:
return nil, trace.Wrap(err)
case privKey := <-keysChan:
privateKeys = append(privateKeys, privKey)
}
}
return privateKeys, nil
}
// PrecomputeKeys sets this package into a mode where a small backlog of keys are
// computed in advance. This should only be enabled if large spikes in key computation
// are expected (e.g. in auth/proxy services). Safe to double-call.
func PrecomputeKeys() {
startPrecomputeOnce.Do(func() {
go precomputeKeys()
})
}
// PrecomputeTestKeys generates RSA keys and reuse them to reduce CPU usage. This method should
// only be in tests. Safe to call multiple times.
// This function takes *testing.M, so is only can be used from TestMain in tests.
func PrecomputeTestKeys(_ *testing.M) {
startPrecomputeOnce.Do(func() {
go precomputeTestKeys()
})
}