mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-01 05:50:30 +08:00
Add strict CA checks on newKeySet() and address test code duplication (#62177)
* Add strict CA checks on newKeySet() and NewTestCAWithConfig() * Consolidate the many copies of NewTestCAWithConfig() * Add authcatest to depguard * Check CAKeySet before returning * Appease linter (authtest can import test packages) * Rename types, drop TODO * Remove panics from authcatest * Propagate errors on lib/cache * Remove lib/cache.NewTestCA alias * Remove lib/authtest.NewCAWithConfig alias * Remove lib/authtest.CAConfig alias * Remove the lib/authtest.NewCA alias * Remove lib/services/local.NewTestCA alias
This commit is contained in:
+6
-3
@@ -192,17 +192,18 @@ linters:
|
||||
test_packages:
|
||||
files:
|
||||
- '!$test'
|
||||
- '!**/integrations/operator/controllers/resources/testlib/**.go'
|
||||
- '!**/e/lib/aws/identitycenter/test/**'
|
||||
- '!**/e/lib/devicetrust/testenv/**'
|
||||
- '!**/e/lib/jamf/testenv/**'
|
||||
- '!**/e/lib/intune/testenv/**'
|
||||
- '!**/e/lib/idp/saml/testenv/**'
|
||||
- '!**/e/lib/intune/testenv/**'
|
||||
- '!**/e/lib/jamf/testenv/**'
|
||||
- '!**/e/lib/operatortest/**'
|
||||
- '!**/e/tests/**'
|
||||
- '!**/integration/db/fixture.go'
|
||||
- '!**/integration/helpers/**'
|
||||
- '!**/integrations/lib/testing/**'
|
||||
- '!**/integrations/operator/controllers/resources/testlib/**.go'
|
||||
- '!**/lib/auth/authtest/**'
|
||||
deny:
|
||||
- pkg: github.com/gravitational/teleport/e/lib/aws/identitycenter/test
|
||||
desc: testing packages should not be imported outside of _test.go files
|
||||
@@ -230,6 +231,8 @@ linters:
|
||||
desc: testing packages should not be imported outside of _test.go files
|
||||
- pkg: github.com/gravitational/teleport/lib/utils/logtesttest
|
||||
desc: testing packages should not be imported outside of _test.go files
|
||||
- pkg: github.com/gravitational/teleport/lib/auth/authcatest
|
||||
desc: testing packages should not be imported outside of _test.go files
|
||||
- pkg: github.com/gravitational/teleport/lib/auth/authtest
|
||||
desc: testing packages should not be imported outside of _test.go files
|
||||
- pkg: github.com/gravitational/teleport/lib/cryptosuites/cryptosuitestest
|
||||
|
||||
+26
-1
@@ -8247,8 +8247,28 @@ func (a *Server) addAdditionalTrustedKeysAtomic(ctx context.Context, ca types.Ce
|
||||
}
|
||||
|
||||
// newKeySet generates a new sets of keys for a given CA type.
|
||||
// Keep this function in sync with lib/services/suite/suite.go:NewTestCAWithConfig().
|
||||
// Keep this function in sync with lib/auth/authcatest.NewTestCAWithConfig().
|
||||
func newKeySet(ctx context.Context, keyStore *keystore.Manager, caID types.CertAuthID) (types.CAKeySet, error) {
|
||||
switch caID.Type {
|
||||
case
|
||||
types.HostCA,
|
||||
types.UserCA,
|
||||
types.DatabaseCA,
|
||||
types.DatabaseClientCA,
|
||||
types.OpenSSHCA,
|
||||
types.JWTSigner,
|
||||
types.SAMLIDPCA,
|
||||
types.OIDCIdPCA,
|
||||
types.SPIFFECA,
|
||||
types.OktaCA,
|
||||
types.AWSRACA,
|
||||
types.BoundKeypairCA:
|
||||
// OK, known CA type.
|
||||
default:
|
||||
return types.CAKeySet{}, trace.BadParameter(
|
||||
"cannot generate new key set for unknown CA type %q", caID.Type)
|
||||
}
|
||||
|
||||
var keySet types.CAKeySet
|
||||
|
||||
// Add SSH keys if necessary.
|
||||
@@ -8281,6 +8301,11 @@ func newKeySet(ctx context.Context, keyStore *keystore.Manager, caID types.CertA
|
||||
keySet.JWT = append(keySet.JWT, jwtKeyPair)
|
||||
}
|
||||
|
||||
// Sanity check that the key set has at least one key.
|
||||
if len(keySet.SSH) == 0 && len(keySet.TLS) == 0 && len(keySet.JWT) == 0 {
|
||||
return types.CAKeySet{}, trace.BadParameter("no keys generated for CA type %q", caID.Type)
|
||||
}
|
||||
|
||||
return keySet, nil
|
||||
}
|
||||
|
||||
|
||||
+23
-11
@@ -68,6 +68,7 @@ import (
|
||||
"github.com/gravitational/teleport/api/utils/sshutils"
|
||||
"github.com/gravitational/teleport/entitlements"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/auth/authtest"
|
||||
"github.com/gravitational/teleport/lib/auth/keystore"
|
||||
@@ -216,14 +217,19 @@ func newTestPack(ctx context.Context, opts testPackOptions) (p testPack, err err
|
||||
return testPack{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
if err := p.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.UserCA, p.clusterName.GetClusterName())); err != nil {
|
||||
return testPack{}, trace.Wrap(err)
|
||||
}
|
||||
if err := p.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.HostCA, p.clusterName.GetClusterName())); err != nil {
|
||||
return testPack{}, trace.Wrap(err)
|
||||
}
|
||||
if err := p.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.OpenSSHCA, p.clusterName.GetClusterName())); err != nil {
|
||||
return testPack{}, trace.Wrap(err)
|
||||
clusterName := p.clusterName.GetClusterName()
|
||||
for _, caType := range []types.CertAuthType{
|
||||
types.UserCA,
|
||||
types.HostCA,
|
||||
types.OpenSSHCA,
|
||||
} {
|
||||
ca, err := authcatest.NewCA(caType, clusterName)
|
||||
if err != nil {
|
||||
return testPack{}, trace.Wrap(err)
|
||||
}
|
||||
if err := p.a.UpsertCertAuthority(ctx, ca); err != nil {
|
||||
return testPack{}, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
@@ -1315,8 +1321,12 @@ func TestTrustedClusterCRUDEventEmitted(t *testing.T) {
|
||||
_, err = s.a.Services.UpsertTrustedCluster(ctx, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, s.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.UserCA, "test")))
|
||||
require.NoError(t, s.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.HostCA, "test")))
|
||||
userCA, err := authcatest.NewCA(types.UserCA, "test")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.a.UpsertCertAuthority(ctx, userCA))
|
||||
hostCA, err := authcatest.NewCA(types.HostCA, "test")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.a.UpsertCertAuthority(ctx, hostCA))
|
||||
|
||||
err = s.a.CreateReverseTunnel(ctx, tc)
|
||||
require.NoError(t, err)
|
||||
@@ -4047,7 +4057,9 @@ func TestCAGeneration(t *testing.T) {
|
||||
|
||||
for _, caType := range types.CertAuthTypes {
|
||||
t.Run(string(caType), func(t *testing.T) {
|
||||
testKeySet := authtest.NewTestCA(caType, clusterName, privKey).Spec.ActiveKeys
|
||||
ca, err := authcatest.NewCA(caType, clusterName, privKey)
|
||||
require.NoError(t, err)
|
||||
testKeySet := ca.Spec.ActiveKeys
|
||||
keySet, err := auth.NewKeySet(ctx, keyStoreManager, types.CertAuthID{Type: caType, DomainName: clusterName})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2025 Gravitational, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package authcatest holds helpers to create CAs for testing.
|
||||
package authcatest
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/api/utils/keys"
|
||||
"github.com/gravitational/teleport/lib/cryptosuites"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/fixtures"
|
||||
"github.com/gravitational/teleport/lib/tlsca"
|
||||
)
|
||||
|
||||
// CAConfig defines the configuration for generating a test certificate
|
||||
// authority.
|
||||
type CAConfig struct {
|
||||
Type types.CertAuthType
|
||||
PrivateKeys [][]byte
|
||||
Clock clockwork.Clock
|
||||
ClusterName string
|
||||
// the below string fields default to ClusterName if left empty
|
||||
ResourceName string
|
||||
SubjectOrganization string
|
||||
}
|
||||
|
||||
// NewCA returns new test authority with a test key as a public and signing key.
|
||||
func NewCA(
|
||||
caType types.CertAuthType,
|
||||
clusterName string,
|
||||
privateKeys ...[]byte,
|
||||
) (*types.CertAuthorityV2, error) {
|
||||
return NewCAWithConfig(CAConfig{
|
||||
Type: caType,
|
||||
ClusterName: clusterName,
|
||||
PrivateKeys: privateKeys,
|
||||
Clock: clockwork.NewRealClock(),
|
||||
})
|
||||
}
|
||||
|
||||
// NewCAWithConfig generates a new certificate authority with the specified
|
||||
// configuration.
|
||||
//
|
||||
// Keep this function in-sync with lib/auth.newKeySet().
|
||||
func NewCAWithConfig(config CAConfig) (*types.CertAuthorityV2, error) {
|
||||
switch config.Type {
|
||||
case
|
||||
types.HostCA,
|
||||
types.UserCA,
|
||||
types.DatabaseCA,
|
||||
types.DatabaseClientCA,
|
||||
types.OpenSSHCA,
|
||||
types.JWTSigner,
|
||||
types.SAMLIDPCA,
|
||||
types.OIDCIdPCA,
|
||||
types.SPIFFECA,
|
||||
types.OktaCA,
|
||||
types.AWSRACA,
|
||||
types.BoundKeypairCA:
|
||||
// OK, known CA type.
|
||||
default:
|
||||
return nil, trace.BadParameter("cannot generate new key set for unknown CA type %q", config.Type)
|
||||
}
|
||||
|
||||
var keyPEM []byte
|
||||
var key *keys.PrivateKey
|
||||
|
||||
if config.ResourceName == "" {
|
||||
config.ResourceName = config.ClusterName
|
||||
}
|
||||
if config.SubjectOrganization == "" {
|
||||
config.SubjectOrganization = config.ClusterName
|
||||
}
|
||||
|
||||
switch config.Type {
|
||||
case types.DatabaseCA, types.SAMLIDPCA, types.OIDCIdPCA:
|
||||
// These CAs only support RSA.
|
||||
keyPEM = fixtures.PEMBytes["rsa"]
|
||||
case types.DatabaseClientCA:
|
||||
// The db client CA also only supports RSA, but some tests rely on it
|
||||
// being different than the DB CA.
|
||||
keyPEM = fixtures.PEMBytes["rsa-db-client"]
|
||||
}
|
||||
if len(config.PrivateKeys) > 0 {
|
||||
// Allow test to override the private key.
|
||||
keyPEM = config.PrivateKeys[0]
|
||||
}
|
||||
|
||||
if keyPEM != nil {
|
||||
var err error
|
||||
key, err = keys.ParsePrivateKey(keyPEM)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
} else {
|
||||
// If config.PrivateKeys was not set and this CA does not exclusively
|
||||
// support RSA, generate an ECDSA key. Signatures are ~10x faster than
|
||||
// RSA and generating a new key is actually faster than parsing a PEM
|
||||
// fixture.
|
||||
signer, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
key, err = keys.NewPrivateKey(signer)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
keyPEM = key.PrivateKeyPEM()
|
||||
}
|
||||
|
||||
ca := &types.CertAuthorityV2{
|
||||
Kind: types.KindCertAuthority,
|
||||
SubKind: string(config.Type),
|
||||
Version: types.V2,
|
||||
Metadata: types.Metadata{
|
||||
Name: config.ResourceName,
|
||||
Namespace: apidefaults.Namespace,
|
||||
},
|
||||
Spec: types.CertAuthoritySpecV2{
|
||||
Type: config.Type,
|
||||
ClusterName: config.ClusterName,
|
||||
},
|
||||
}
|
||||
|
||||
// Add SSH keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.OpenSSHCA:
|
||||
ca.Spec.ActiveKeys.SSH = []*types.SSHKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: key.MarshalSSHPublicKey(),
|
||||
}}
|
||||
}
|
||||
|
||||
// Add TLS keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.DatabaseCA, types.DatabaseClientCA, types.SAMLIDPCA, types.SPIFFECA, types.AWSRACA:
|
||||
cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
|
||||
Signer: key.Signer,
|
||||
Entity: pkix.Name{
|
||||
CommonName: config.ClusterName,
|
||||
Organization: []string{config.SubjectOrganization},
|
||||
},
|
||||
TTL: defaults.CATTL,
|
||||
Clock: config.Clock,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.TLS = []*types.TLSKeyPair{{
|
||||
Key: keyPEM,
|
||||
Cert: cert,
|
||||
}}
|
||||
}
|
||||
|
||||
// Add JWT keys if necessary.
|
||||
switch config.Type {
|
||||
case types.JWTSigner, types.OIDCIdPCA, types.SPIFFECA, types.OktaCA, types.BoundKeypairCA:
|
||||
pubKeyPEM, err := keys.MarshalPublicKey(key.Public())
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.JWT = []*types.JWTKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: pubKeyPEM,
|
||||
}}
|
||||
}
|
||||
|
||||
// Sanity check that the CA has at least one active key.
|
||||
aks := ca.Spec.ActiveKeys
|
||||
if len(aks.SSH) == 0 && len(aks.TLS) == 0 && len(aks.JWT) == 0 {
|
||||
return nil, trace.BadParameter("no keys generated for CA type %q", config.Type)
|
||||
}
|
||||
|
||||
return ca, nil
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -41,12 +40,12 @@ import (
|
||||
"github.com/gravitational/teleport/api/client"
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
apiutils "github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/teleport/api/utils/keys"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/accesspoint"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/auth/recordingmetadata"
|
||||
"github.com/gravitational/teleport/lib/auth/state"
|
||||
@@ -60,7 +59,6 @@ import (
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/events"
|
||||
"github.com/gravitational/teleport/lib/events/eventstest"
|
||||
"github.com/gravitational/teleport/lib/fixtures"
|
||||
"github.com/gravitational/teleport/lib/limiter"
|
||||
"github.com/gravitational/teleport/lib/service/servicecfg"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
@@ -415,11 +413,15 @@ func NewAuthServer(cfg AuthServerConfig) (*AuthServer, error) {
|
||||
|
||||
// Setup certificate and signing authorities.
|
||||
for _, caType := range types.CertAuthTypes {
|
||||
if err = srv.AuthServer.UpsertCertAuthority(ctx, NewTestCAWithConfig(TestCAConfig{
|
||||
ca, err := authcatest.NewCAWithConfig(authcatest.CAConfig{
|
||||
Type: caType,
|
||||
ClusterName: srv.ClusterName,
|
||||
Clock: cfg.Clock,
|
||||
})); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if err = srv.AuthServer.UpsertCertAuthority(ctx, ca); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
@@ -1603,137 +1605,3 @@ func FlushCache(t *testing.T, clt Flusher) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCAConfig defines the configuration for generating
|
||||
// a test certificate authority
|
||||
type TestCAConfig struct {
|
||||
Type types.CertAuthType
|
||||
PrivateKeys [][]byte
|
||||
Clock clockwork.Clock
|
||||
ClusterName string
|
||||
// the below string fields default to ClusterName if left empty
|
||||
ResourceName string
|
||||
SubjectOrganization string
|
||||
}
|
||||
|
||||
// NewTestCA returns new test authority with a test key as a public and
|
||||
// signing key
|
||||
func NewTestCA(caType types.CertAuthType, clusterName string, privateKeys ...[]byte) *types.CertAuthorityV2 {
|
||||
return NewTestCAWithConfig(TestCAConfig{
|
||||
Type: caType,
|
||||
ClusterName: clusterName,
|
||||
PrivateKeys: privateKeys,
|
||||
Clock: clockwork.NewRealClock(),
|
||||
})
|
||||
}
|
||||
|
||||
// NewTestCAWithConfig generates a new certificate authority with the specified
|
||||
// configuration
|
||||
// Keep this function in-sync with lib/auth/auth.go:newKeySet().
|
||||
// TODO(jakule): reuse keystore.KeyStore interface to match newKeySet().
|
||||
func NewTestCAWithConfig(config TestCAConfig) *types.CertAuthorityV2 {
|
||||
var keyPEM []byte
|
||||
var key *keys.PrivateKey
|
||||
|
||||
if config.ResourceName == "" {
|
||||
config.ResourceName = config.ClusterName
|
||||
}
|
||||
if config.SubjectOrganization == "" {
|
||||
config.SubjectOrganization = config.ClusterName
|
||||
}
|
||||
|
||||
switch config.Type {
|
||||
case types.DatabaseCA, types.SAMLIDPCA, types.OIDCIdPCA:
|
||||
// These CAs only support RSA.
|
||||
keyPEM = fixtures.PEMBytes["rsa"]
|
||||
case types.DatabaseClientCA:
|
||||
// The db client CA also only supports RSA, but some tests rely on it
|
||||
// being different than the DB CA.
|
||||
keyPEM = fixtures.PEMBytes["rsa-db-client"]
|
||||
}
|
||||
if len(config.PrivateKeys) > 0 {
|
||||
// Allow test to override the private key.
|
||||
keyPEM = config.PrivateKeys[0]
|
||||
}
|
||||
|
||||
if keyPEM != nil {
|
||||
var err error
|
||||
key, err = keys.ParsePrivateKey(keyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
// If config.PrivateKeys was not set and this CA does not exclusively
|
||||
// support RSA, generate an ECDSA key. Signatures are ~10x faster than
|
||||
// RSA and generating a new key is actually faster than parsing a PEM
|
||||
// fixture.
|
||||
signer, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
key, err = keys.NewPrivateKey(signer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyPEM = key.PrivateKeyPEM()
|
||||
}
|
||||
|
||||
ca := &types.CertAuthorityV2{
|
||||
Kind: types.KindCertAuthority,
|
||||
SubKind: string(config.Type),
|
||||
Version: types.V2,
|
||||
Metadata: types.Metadata{
|
||||
Name: config.ResourceName,
|
||||
Namespace: apidefaults.Namespace,
|
||||
},
|
||||
Spec: types.CertAuthoritySpecV2{
|
||||
Type: config.Type,
|
||||
ClusterName: config.ClusterName,
|
||||
},
|
||||
}
|
||||
|
||||
// Add SSH keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.OpenSSHCA:
|
||||
ca.Spec.ActiveKeys.SSH = []*types.SSHKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: key.MarshalSSHPublicKey(),
|
||||
}}
|
||||
}
|
||||
|
||||
// Add TLS keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.DatabaseCA, types.DatabaseClientCA, types.SAMLIDPCA, types.SPIFFECA, types.AWSRACA:
|
||||
cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
|
||||
Signer: key.Signer,
|
||||
Entity: pkix.Name{
|
||||
CommonName: config.ClusterName,
|
||||
Organization: []string{config.SubjectOrganization},
|
||||
},
|
||||
TTL: defaults.CATTL,
|
||||
Clock: config.Clock,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.TLS = []*types.TLSKeyPair{{
|
||||
Key: keyPEM,
|
||||
Cert: cert,
|
||||
}}
|
||||
}
|
||||
|
||||
// Add JWT keys if necessary.
|
||||
switch config.Type {
|
||||
case types.JWTSigner, types.OIDCIdPCA, types.SPIFFECA, types.OktaCA, types.BoundKeypairCA:
|
||||
pubKeyPEM, err := keys.MarshalPublicKey(key.Public())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.JWT = []*types.JWTKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: pubKeyPEM,
|
||||
}}
|
||||
}
|
||||
|
||||
return ca
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import (
|
||||
"github.com/gravitational/teleport/entitlements"
|
||||
"github.com/gravitational/teleport/lib"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/auth/authtest"
|
||||
"github.com/gravitational/teleport/lib/auth/state"
|
||||
"github.com/gravitational/teleport/lib/auth/storage"
|
||||
@@ -228,11 +229,14 @@ func TestSignatureAlgorithmSuite(t *testing.T) {
|
||||
}
|
||||
// Pre-generate all CAs to keep tests fast esp. with SoftHSM.
|
||||
for _, caType := range types.CertAuthTypes {
|
||||
cfg.BootstrapResources = append(cfg.BootstrapResources, authtest.NewTestCAWithConfig(authtest.TestCAConfig{
|
||||
ca, err := authcatest.NewCAWithConfig(authcatest.CAConfig{
|
||||
Type: caType,
|
||||
ClusterName: cfg.ClusterName.GetClusterName(),
|
||||
Clock: cfg.Clock,
|
||||
}))
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg.BootstrapResources = append(cfg.BootstrapResources, ca)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
+18
-14
@@ -42,6 +42,7 @@ import (
|
||||
apievents "github.com/gravitational/teleport/api/types/events"
|
||||
wanpb "github.com/gravitational/teleport/api/types/webauthn"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/auth/authtest"
|
||||
authority "github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
@@ -906,14 +907,20 @@ func (s *passwordSuite) prepareForPasswordChange(user string, pass []byte, secon
|
||||
OldPassword: pass,
|
||||
}
|
||||
|
||||
err := s.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.UserCA, "me.localhost"))
|
||||
userCA, err := authcatest.NewCA(types.UserCA, "me.localhost")
|
||||
if err != nil {
|
||||
return req, err
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if err := s.a.UpsertCertAuthority(ctx, userCA); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.a.UpsertCertAuthority(ctx, authtest.NewTestCA(types.HostCA, "me.localhost"))
|
||||
hostCA, err := authcatest.NewCA(types.HostCA, "me.localhost")
|
||||
if err != nil {
|
||||
return req, err
|
||||
return nil, err
|
||||
}
|
||||
if err := s.a.UpsertCertAuthority(ctx, hostCA); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ap, err := types.NewAuthPreference(types.AuthPreferenceSpecV2{
|
||||
@@ -921,21 +928,18 @@ func (s *passwordSuite) prepareForPasswordChange(user string, pass []byte, secon
|
||||
SecondFactor: secondFactorType,
|
||||
})
|
||||
if err != nil {
|
||||
return req, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = s.a.UpsertAuthPreference(ctx, ap)
|
||||
if err != nil {
|
||||
return req, err
|
||||
if _, err := s.a.UpsertAuthPreference(ctx, ap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, _, err = authtest.CreateUserAndRole(s.a, user, []string{user}, nil)
|
||||
if err != nil {
|
||||
return req, err
|
||||
if _, _, err := authtest.CreateUserAndRole(s.a, user, []string{user}, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = s.a.UpsertPassword(user, pass)
|
||||
if err != nil {
|
||||
return req, err
|
||||
if err := s.a.UpsertPassword(user, pass); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
|
||||
+13
-6
@@ -63,6 +63,7 @@ import (
|
||||
"github.com/gravitational/teleport/api/utils/keys"
|
||||
"github.com/gravitational/teleport/api/utils/sshutils"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/auth/authtest"
|
||||
"github.com/gravitational/teleport/lib/auth/state"
|
||||
@@ -4380,7 +4381,8 @@ func TestEvents(t *testing.T) {
|
||||
LoadSecrets: true,
|
||||
},
|
||||
crud: func(context.Context) types.Resource {
|
||||
ca := authtest.NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, testSrv.Auth().UpsertCertAuthority(ctx, ca))
|
||||
|
||||
out, err := testSrv.Auth().GetCertAuthority(ctx, *ca.ID(), true)
|
||||
@@ -4401,7 +4403,8 @@ func TestEvents(t *testing.T) {
|
||||
LoadSecrets: false,
|
||||
},
|
||||
crud: func(context.Context) types.Resource {
|
||||
ca := authtest.NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, testSrv.Auth().UpsertCertAuthority(ctx, ca))
|
||||
|
||||
out, err := testSrv.Auth().GetCertAuthority(ctx, *ca.ID(), false)
|
||||
@@ -5748,10 +5751,14 @@ func TestVerifyPeerCert(t *testing.T) {
|
||||
}
|
||||
)
|
||||
|
||||
localHostCA := authtest.NewTestCA(types.HostCA, localClusterName)
|
||||
remoteHostCA := authtest.NewTestCA(types.HostCA, remoteClusterName)
|
||||
localUserCA := authtest.NewTestCA(types.UserCA, localClusterName)
|
||||
remoteUserCA := authtest.NewTestCA(types.UserCA, remoteClusterName)
|
||||
localHostCA, err := authcatest.NewCA(types.HostCA, localClusterName)
|
||||
require.NoError(t, err)
|
||||
remoteHostCA, err := authcatest.NewCA(types.HostCA, remoteClusterName)
|
||||
require.NoError(t, err)
|
||||
localUserCA, err := authcatest.NewCA(types.UserCA, localClusterName)
|
||||
require.NoError(t, err)
|
||||
remoteUserCA, err := authcatest.NewCA(types.UserCA, remoteClusterName)
|
||||
require.NoError(t, err)
|
||||
|
||||
caPool := buildPoolInfo(
|
||||
t,
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/auth/authtest"
|
||||
authority "github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
@@ -247,34 +248,38 @@ func TestValidateTrustedCluster(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("more than one CA", func(t *testing.T) {
|
||||
ca1, err := authcatest.NewCA(types.HostCA, "rc1")
|
||||
require.NoError(t, err)
|
||||
ca2, err := authcatest.NewCA(types.HostCA, "rc2")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{
|
||||
authtest.NewTestCA(types.HostCA, "rc1"),
|
||||
authtest.NewTestCA(types.HostCA, "rc2"),
|
||||
},
|
||||
CAs: []types.CertAuthority{ca1, ca2},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected exactly one")
|
||||
})
|
||||
|
||||
t.Run("wrong CA type", func(t *testing.T) {
|
||||
ca, err := authcatest.NewCA(types.UserCA, "rc3")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{
|
||||
authtest.NewTestCA(types.UserCA, "rc3"),
|
||||
},
|
||||
CAs: []types.CertAuthority{ca},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected host certificate authority")
|
||||
})
|
||||
|
||||
t.Run("wrong CA name", func(t *testing.T) {
|
||||
ca, err := authcatest.NewCA(types.HostCA, localClusterName)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{
|
||||
authtest.NewTestCA(types.HostCA, localClusterName),
|
||||
},
|
||||
CAs: []types.CertAuthority{ca},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "same name as this cluster")
|
||||
@@ -289,18 +294,21 @@ func TestValidateTrustedCluster(t *testing.T) {
|
||||
_, err = a.Services.UpsertTrustedCluster(ctx, trustedCluster)
|
||||
require.NoError(t, err)
|
||||
|
||||
ca, err := authcatest.NewCA(types.HostCA, trustedCluster.GetName())
|
||||
require.NoError(t, err)
|
||||
_, err = a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{
|
||||
authtest.NewTestCA(types.HostCA, trustedCluster.GetName()),
|
||||
},
|
||||
CAs: []types.CertAuthority{ca},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "same name as trusted cluster")
|
||||
})
|
||||
|
||||
t.Run("all CAs are returned when v10+", func(t *testing.T) {
|
||||
leafClusterCA := types.CertAuthority(authtest.NewTestCA(types.HostCA, "leafcluster-1"))
|
||||
ca, err := authcatest.NewCA(types.HostCA, "leafcluster-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
leafClusterCA := types.CertAuthority(ca)
|
||||
resp, err := a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{leafClusterCA},
|
||||
@@ -375,7 +383,10 @@ func TestValidateTrustedCluster(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Host User and Database CA are returned by default", func(t *testing.T) {
|
||||
leafClusterCA := types.CertAuthority(authtest.NewTestCA(types.HostCA, "leafcluster-2"))
|
||||
ca, err := authcatest.NewCA(types.HostCA, "leafcluster-2")
|
||||
require.NoError(t, err)
|
||||
|
||||
leafClusterCA := types.CertAuthority(ca)
|
||||
resp, err := a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{leafClusterCA},
|
||||
@@ -406,15 +417,16 @@ func TestValidateTrustedCluster(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("CA cluster name does not match subject organization", func(t *testing.T) {
|
||||
ca, err := authcatest.NewCAWithConfig(authcatest.CAConfig{
|
||||
Type: types.HostCA,
|
||||
ClusterName: "remoteCluster",
|
||||
SubjectOrganization: "commonName",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = a.ValidateTrustedCluster(ctx, &authclient.ValidateTrustedClusterRequest{
|
||||
Token: validToken,
|
||||
CAs: []types.CertAuthority{
|
||||
authtest.NewTestCAWithConfig(authtest.TestCAConfig{
|
||||
Type: types.HostCA,
|
||||
ClusterName: "remoteCluster",
|
||||
SubjectOrganization: "commonName",
|
||||
}),
|
||||
},
|
||||
CAs: []types.CertAuthority{ca},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "the subject organization of a CA certificate does not match the cluster name of the CA")
|
||||
@@ -500,7 +512,8 @@ func TestUpsertTrustedCluster(t *testing.T) {
|
||||
trustedCluster, err := types.NewTrustedCluster("trustedcluster", trustedClusterSpec)
|
||||
require.NoError(t, err)
|
||||
|
||||
ca := authtest.NewTestCA(types.UserCA, "trustedcluster")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "trustedcluster")
|
||||
require.NoError(t, err)
|
||||
|
||||
auth.ConfigureCAsForTrustedCluster(trustedCluster, []types.CertAuthority{ca})
|
||||
|
||||
@@ -639,7 +652,8 @@ func TestUpdateTrustedCluster(t *testing.T) {
|
||||
trustedCluster, err := types.NewTrustedCluster(testClusterName, trustedClusterSpec)
|
||||
require.NoError(t, err)
|
||||
|
||||
ca := authtest.NewTestCA(types.UserCA, testClusterName)
|
||||
ca, err := authcatest.NewCA(types.UserCA, testClusterName)
|
||||
require.NoError(t, err)
|
||||
|
||||
auth.ConfigureCAsForTrustedCluster(trustedCluster, []types.CertAuthority{ca})
|
||||
|
||||
|
||||
Vendored
+15
-146
@@ -20,7 +20,6 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"iter"
|
||||
"log/slog"
|
||||
@@ -76,13 +75,11 @@ import (
|
||||
"github.com/gravitational/teleport/api/types/userprovisioning"
|
||||
"github.com/gravitational/teleport/api/types/usertasks"
|
||||
"github.com/gravitational/teleport/api/utils/clientutils"
|
||||
"github.com/gravitational/teleport/api/utils/keys"
|
||||
"github.com/gravitational/teleport/entitlements"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/backend/memory"
|
||||
"github.com/gravitational/teleport/lib/cryptosuites"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/fixtures"
|
||||
"github.com/gravitational/teleport/lib/itertools/stream"
|
||||
"github.com/gravitational/teleport/lib/modules"
|
||||
"github.com/gravitational/teleport/lib/modules/modulestest"
|
||||
@@ -90,7 +87,6 @@ import (
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/services/local"
|
||||
"github.com/gravitational/teleport/lib/srv/db/common/databaseobject"
|
||||
"github.com/gravitational/teleport/lib/tlsca"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/teleport/lib/utils/log/logtest"
|
||||
)
|
||||
@@ -648,7 +644,8 @@ func TestWatchers(t *testing.T) {
|
||||
t.Fatalf("Timeout waiting for event.")
|
||||
}
|
||||
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, ca))
|
||||
|
||||
select {
|
||||
@@ -707,7 +704,8 @@ func TestWatchers(t *testing.T) {
|
||||
|
||||
// this ca will not be matched by our filter, so the same reasoning applies
|
||||
// as we upsert it and delete it
|
||||
filteredCa := NewTestCA(types.HostCA, "example.net")
|
||||
filteredCa, err := authcatest.NewCA(types.HostCA, "example.net")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, filteredCa))
|
||||
require.NoError(t, p.trustS.DeleteCertAuthority(ctx, filteredCa.GetID()))
|
||||
|
||||
@@ -802,7 +800,8 @@ func TestCompletenessInit(t *testing.T) {
|
||||
|
||||
// put lots of CAs in the backend
|
||||
for i := range caCount {
|
||||
ca := NewTestCA(types.UserCA, fmt.Sprintf("%d.example.com", i))
|
||||
ca, err := authcatest.NewCA(types.UserCA, fmt.Sprintf("%d.example.com", i))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, ca))
|
||||
}
|
||||
|
||||
@@ -895,7 +894,8 @@ func TestCompletenessReset(t *testing.T) {
|
||||
|
||||
// put lots of CAs in the backend
|
||||
for i := range caCount {
|
||||
ca := NewTestCA(types.UserCA, fmt.Sprintf("%d.example.com", i))
|
||||
ca, err := authcatest.NewCA(types.UserCA, fmt.Sprintf("%d.example.com", i))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, ca))
|
||||
}
|
||||
|
||||
@@ -1217,7 +1217,8 @@ func initStrategy(t *testing.T) {
|
||||
_, err = p.cache.GetCertAuthorities(ctx, types.UserCA, false)
|
||||
require.True(t, trace.IsConnectionProblem(err))
|
||||
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
// NOTE 1: this could produce event processed
|
||||
// below, based on whether watcher restarts to get the event
|
||||
// or not, which is normal, but has to be accounted below
|
||||
@@ -1279,7 +1280,8 @@ func TestRecovery(t *testing.T) {
|
||||
p := newPackForAuth(t)
|
||||
t.Cleanup(p.Close)
|
||||
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, ca))
|
||||
|
||||
select {
|
||||
@@ -1298,7 +1300,8 @@ func TestRecovery(t *testing.T) {
|
||||
waitForRestart(t, p.eventsC)
|
||||
|
||||
// add modification and expect the resource to recover
|
||||
ca2 := NewTestCA(types.UserCA, "example2.com")
|
||||
ca2, err := authcatest.NewCA(types.UserCA, "example2.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, ca2))
|
||||
|
||||
// wait for watcher to receive an event
|
||||
@@ -2805,140 +2808,6 @@ func listResource(ctx context.Context, lister resourcesLister, kind string, page
|
||||
return resp.Resources, resp.NextKey, nil
|
||||
}
|
||||
|
||||
// NewTestCA returns new test authority with a test key as a public and
|
||||
// signing key
|
||||
func NewTestCA(caType types.CertAuthType, clusterName string, privateKeys ...[]byte) *types.CertAuthorityV2 {
|
||||
return NewTestCAWithConfig(TestCAConfig{
|
||||
Type: caType,
|
||||
ClusterName: clusterName,
|
||||
PrivateKeys: privateKeys,
|
||||
Clock: clockwork.NewRealClock(),
|
||||
})
|
||||
}
|
||||
|
||||
// TestCAConfig defines the configuration for generating
|
||||
// a test certificate authority
|
||||
type TestCAConfig struct {
|
||||
Type types.CertAuthType
|
||||
PrivateKeys [][]byte
|
||||
Clock clockwork.Clock
|
||||
ClusterName string
|
||||
// the below string fields default to ClusterName if left empty
|
||||
ResourceName string
|
||||
SubjectOrganization string
|
||||
}
|
||||
|
||||
// NewTestCAWithConfig generates a new certificate authority with the specified
|
||||
// configuration
|
||||
// Keep this function in-sync with lib/auth/auth.go:newKeySet().
|
||||
// TODO(jakule): reuse keystore.KeyStore interface to match newKeySet().
|
||||
func NewTestCAWithConfig(config TestCAConfig) *types.CertAuthorityV2 {
|
||||
var keyPEM []byte
|
||||
var key *keys.PrivateKey
|
||||
|
||||
if config.ResourceName == "" {
|
||||
config.ResourceName = config.ClusterName
|
||||
}
|
||||
if config.SubjectOrganization == "" {
|
||||
config.SubjectOrganization = config.ClusterName
|
||||
}
|
||||
|
||||
switch config.Type {
|
||||
case types.DatabaseCA, types.SAMLIDPCA, types.OIDCIdPCA:
|
||||
// These CAs only support RSA.
|
||||
keyPEM = fixtures.PEMBytes["rsa"]
|
||||
case types.DatabaseClientCA:
|
||||
// The db client CA also only supports RSA, but some tests rely on it
|
||||
// being different than the DB CA.
|
||||
keyPEM = fixtures.PEMBytes["rsa-db-client"]
|
||||
}
|
||||
if len(config.PrivateKeys) > 0 {
|
||||
// Allow test to override the private key.
|
||||
keyPEM = config.PrivateKeys[0]
|
||||
}
|
||||
|
||||
if keyPEM != nil {
|
||||
var err error
|
||||
key, err = keys.ParsePrivateKey(keyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
// If config.PrivateKeys was not set and this CA does not exclusively
|
||||
// support RSA, generate an ECDSA key. Signatures are ~10x faster than
|
||||
// RSA and generating a new key is actually faster than parsing a PEM
|
||||
// fixture.
|
||||
signer, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
key, err = keys.NewPrivateKey(signer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyPEM = key.PrivateKeyPEM()
|
||||
}
|
||||
|
||||
ca := &types.CertAuthorityV2{
|
||||
Kind: types.KindCertAuthority,
|
||||
SubKind: string(config.Type),
|
||||
Version: types.V2,
|
||||
Metadata: types.Metadata{
|
||||
Name: config.ResourceName,
|
||||
Namespace: apidefaults.Namespace,
|
||||
},
|
||||
Spec: types.CertAuthoritySpecV2{
|
||||
Type: config.Type,
|
||||
ClusterName: config.ClusterName,
|
||||
},
|
||||
}
|
||||
|
||||
// Add SSH keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.OpenSSHCA:
|
||||
ca.Spec.ActiveKeys.SSH = []*types.SSHKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: key.MarshalSSHPublicKey(),
|
||||
}}
|
||||
}
|
||||
|
||||
// Add TLS keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.DatabaseCA, types.DatabaseClientCA, types.SAMLIDPCA, types.SPIFFECA, types.AWSRACA:
|
||||
cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
|
||||
Signer: key.Signer,
|
||||
Entity: pkix.Name{
|
||||
CommonName: config.ClusterName,
|
||||
Organization: []string{config.SubjectOrganization},
|
||||
},
|
||||
TTL: defaults.CATTL,
|
||||
Clock: config.Clock,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.TLS = []*types.TLSKeyPair{{
|
||||
Key: keyPEM,
|
||||
Cert: cert,
|
||||
}}
|
||||
}
|
||||
|
||||
// Add JWT keys if necessary.
|
||||
switch config.Type {
|
||||
case types.JWTSigner, types.OIDCIdPCA, types.SPIFFECA, types.OktaCA, types.BoundKeypairCA:
|
||||
pubKeyPEM, err := keys.MarshalPublicKey(key.Public())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.JWT = []*types.JWTKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: pubKeyPEM,
|
||||
}}
|
||||
}
|
||||
|
||||
return ca
|
||||
}
|
||||
|
||||
// NewServer creates a new server resource
|
||||
func NewServer(kind, name, addr, namespace string) *types.ServerV2 {
|
||||
return &types.ServerV2{
|
||||
|
||||
Vendored
+15
-7
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
)
|
||||
|
||||
@@ -38,11 +39,14 @@ func TestCA(t *testing.T) {
|
||||
t.Cleanup(p.Close)
|
||||
ctx := context.Background()
|
||||
|
||||
userCA := NewTestCA(types.UserCA, "example.com")
|
||||
userCA, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, userCA))
|
||||
dbCA := NewTestCA(types.DatabaseCA, "example.com")
|
||||
dbCA, err := authcatest.NewCA(types.DatabaseCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, dbCA))
|
||||
dbClientCA := NewTestCA(types.DatabaseClientCA, "example.com")
|
||||
dbClientCA, err := authcatest.NewCA(types.DatabaseClientCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, dbClientCA))
|
||||
const totalCAs = 3
|
||||
|
||||
@@ -129,7 +133,8 @@ func TestNodeCAFiltering(t *testing.T) {
|
||||
require.Equal(t, types.OpInit, fetchEvent().Type)
|
||||
|
||||
// upsert and delete a local host CA, we expect to see a Put and a Delete event
|
||||
localCA := NewTestCA(types.HostCA, "example.com")
|
||||
localCA, err := authcatest.NewCA(types.HostCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, localCA))
|
||||
require.NoError(t, p.trustS.DeleteCertAuthority(ctx, localCA.GetID()))
|
||||
|
||||
@@ -144,7 +149,8 @@ func TestNodeCAFiltering(t *testing.T) {
|
||||
require.Equal(t, "example.com", ev.Resource.GetName())
|
||||
|
||||
// upsert and delete a nonlocal host CA, we expect to only see the Delete event
|
||||
nonlocalCA := NewTestCA(types.HostCA, "example.net")
|
||||
nonlocalCA, err := authcatest.NewCA(types.HostCA, "example.net")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, nonlocalCA))
|
||||
require.NoError(t, p.trustS.DeleteCertAuthority(ctx, nonlocalCA.GetID()))
|
||||
|
||||
@@ -154,7 +160,8 @@ func TestNodeCAFiltering(t *testing.T) {
|
||||
require.Equal(t, "example.net", ev.Resource.GetName())
|
||||
|
||||
// whereas we expect to see the Put and Delete for a trusted *user* CA
|
||||
trustedUserCA := NewTestCA(types.UserCA, "example.net")
|
||||
trustedUserCA, err := authcatest.NewCA(types.UserCA, "example.net")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, trustedUserCA))
|
||||
require.NoError(t, p.trustS.DeleteCertAuthority(ctx, trustedUserCA.GetID()))
|
||||
|
||||
@@ -217,7 +224,8 @@ func TestCAWatcherFilters(t *testing.T) {
|
||||
}
|
||||
|
||||
// generate an OpPut event.
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, p.trustS.UpsertCertAuthority(ctx, ca))
|
||||
|
||||
const fetchTimeout = time.Second
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
|
||||
provisioningv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/provisioning/v1"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/backend/memory"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
@@ -83,9 +84,12 @@ func TestWatchers(t *testing.T) {
|
||||
kind: types.KindCertAuthority,
|
||||
causeEvents: func(subtestCtx context.Context, subtestT *testing.T, backend backend.Backend) {
|
||||
// GIVEN an empty backend, WHEN I create 3 new CAs
|
||||
userCA := NewTestCA(types.UserCA, "example.com")
|
||||
hostCA := NewTestCA(types.HostCA, "example.com")
|
||||
hostCARemote := NewTestCA(types.HostCA, "remote.com")
|
||||
userCA, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
hostCA, err := authcatest.NewCA(types.HostCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
hostCARemote, err := authcatest.NewCA(types.HostCA, "remote.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(subtestT, CreateResources(subtestCtx, backend, userCA, hostCA, hostCARemote))
|
||||
},
|
||||
validateEvents: func(subtestCtx context.Context, subtestT *testing.T, watcher types.Watcher) {
|
||||
@@ -117,9 +121,12 @@ func TestWatchers(t *testing.T) {
|
||||
filter: types.CertAuthorityFilter{types.HostCA: "example.com"}.IntoMap(),
|
||||
causeEvents: func(subtestCtx context.Context, subtestT *testing.T, backend backend.Backend) {
|
||||
// GIVEN an empty backend, WHEN I create some new CAs
|
||||
userCA := NewTestCA(types.UserCA, "example.com")
|
||||
hostCA := NewTestCA(types.HostCA, "example.com")
|
||||
hostCARemote := NewTestCA(types.HostCA, "remote.com")
|
||||
userCA, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
hostCA, err := authcatest.NewCA(types.HostCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
hostCARemote, err := authcatest.NewCA(types.HostCA, "remote.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(subtestT, CreateResources(subtestCtx, backend, userCA, hostCA, hostCARemote))
|
||||
},
|
||||
validateEvents: func(subtestCtx context.Context, subtestT *testing.T, watcher types.Watcher) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
)
|
||||
|
||||
@@ -144,17 +145,17 @@ func TestCertAuthorityResource(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
tt := setupServicesContext(ctx, t)
|
||||
|
||||
userCA := NewTestCA(types.UserCA, "example.com")
|
||||
hostCA := NewTestCA(types.HostCA, "example.com")
|
||||
userCA, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
hostCA, err := authcatest.NewCA(types.HostCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check basic dynamic item creation
|
||||
err := CreateResources(ctx, tt.bk, userCA, hostCA)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, CreateResources(ctx, tt.bk, userCA, hostCA))
|
||||
|
||||
// Check that dynamically created item is compatible with service
|
||||
s := NewCAService(tt.bk)
|
||||
err = s.CompareAndSwapCertAuthority(userCA, userCA)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.CompareAndSwapCertAuthority(userCA, userCA))
|
||||
}
|
||||
|
||||
func TestTrustedClusterResource(t *testing.T) {
|
||||
|
||||
@@ -18,7 +18,6 @@ package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sort"
|
||||
@@ -45,14 +44,12 @@ import (
|
||||
"github.com/gravitational/teleport/api/types/clusterconfig"
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/teleport/api/utils/clientutils"
|
||||
"github.com/gravitational/teleport/api/utils/keys"
|
||||
"github.com/gravitational/teleport/lib/auth/authcatest"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/cryptosuites"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/fixtures"
|
||||
"github.com/gravitational/teleport/lib/itertools/stream"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/tlsca"
|
||||
)
|
||||
|
||||
// ServicesTestSuite is an acceptance test suite
|
||||
@@ -224,7 +221,8 @@ func (s *ServicesTestSuite) LoginAttempts(t *testing.T) {
|
||||
|
||||
func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.TrustS.UpsertCertAuthority(ctx, ca))
|
||||
|
||||
out, err := s.TrustS.GetCertAuthority(ctx, ca.GetID(), true)
|
||||
@@ -250,7 +248,8 @@ func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// test compare and swap
|
||||
ca = NewTestCA(types.UserCA, "example.com")
|
||||
ca, err = authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.TrustS.CreateCertAuthority(ctx, ca))
|
||||
|
||||
clock := clockwork.NewFakeClock()
|
||||
@@ -271,7 +270,8 @@ func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) {
|
||||
require.Empty(t, cmp.Diff(&newCA, out, cmpopts.EquateApproxTime(time.Second), cmpopts.IgnoreFields(types.Metadata{}, "Revision")))
|
||||
|
||||
// test conditional update
|
||||
ca = NewTestCA(types.UserCA, "update.example.com")
|
||||
ca, err = authcatest.NewCA(types.UserCA, "update.example.com")
|
||||
require.NoError(t, err)
|
||||
rev, err := s.TrustInternalS.CreateCertAuthorities(ctx, ca)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -308,7 +308,9 @@ func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) {
|
||||
|
||||
cas = nil
|
||||
for _, cn := range clusterNames {
|
||||
cas = append(cas, NewTestCA(types.UserCA, cn))
|
||||
ca, err := authcatest.NewCA(types.UserCA, cn)
|
||||
require.NoError(t, err)
|
||||
cas = append(cas, ca)
|
||||
}
|
||||
|
||||
rev, err = s.TrustInternalS.CreateCertAuthorities(ctx, cas...)
|
||||
@@ -1996,7 +1998,8 @@ func (s *ServicesTestSuite) Events(t *testing.T) {
|
||||
LoadSecrets: true,
|
||||
},
|
||||
crud: func(context.Context) types.Resource {
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.TrustS.UpsertCertAuthority(ctx, ca))
|
||||
|
||||
out, err := s.TrustS.GetCertAuthority(ctx, *ca.ID(), true)
|
||||
@@ -2017,7 +2020,8 @@ func (s *ServicesTestSuite) Events(t *testing.T) {
|
||||
LoadSecrets: false,
|
||||
},
|
||||
crud: func(context.Context) types.Resource {
|
||||
ca := NewTestCA(types.UserCA, "example.com")
|
||||
ca, err := authcatest.NewCA(types.UserCA, "example.com")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.TrustS.UpsertCertAuthority(ctx, ca))
|
||||
|
||||
out, err := s.TrustS.GetCertAuthority(ctx, *ca.ID(), false)
|
||||
@@ -2541,137 +2545,3 @@ waitLoop:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestCA returns new test authority with a test key as a public and
|
||||
// signing key
|
||||
func NewTestCA(caType types.CertAuthType, clusterName string, privateKeys ...[]byte) *types.CertAuthorityV2 {
|
||||
return NewTestCAWithConfig(TestCAConfig{
|
||||
Type: caType,
|
||||
ClusterName: clusterName,
|
||||
PrivateKeys: privateKeys,
|
||||
Clock: clockwork.NewRealClock(),
|
||||
})
|
||||
}
|
||||
|
||||
// TestCAConfig defines the configuration for generating
|
||||
// a test certificate authority
|
||||
type TestCAConfig struct {
|
||||
Type types.CertAuthType
|
||||
PrivateKeys [][]byte
|
||||
Clock clockwork.Clock
|
||||
ClusterName string
|
||||
// the below string fields default to ClusterName if left empty
|
||||
ResourceName string
|
||||
SubjectOrganization string
|
||||
}
|
||||
|
||||
// NewTestCAWithConfig generates a new certificate authority with the specified
|
||||
// configuration
|
||||
// Keep this function in-sync with lib/auth/auth.go:newKeySet().
|
||||
// TODO(jakule): reuse keystore.KeyStore interface to match newKeySet().
|
||||
func NewTestCAWithConfig(config TestCAConfig) *types.CertAuthorityV2 {
|
||||
var keyPEM []byte
|
||||
var key *keys.PrivateKey
|
||||
|
||||
if config.ResourceName == "" {
|
||||
config.ResourceName = config.ClusterName
|
||||
}
|
||||
if config.SubjectOrganization == "" {
|
||||
config.SubjectOrganization = config.ClusterName
|
||||
}
|
||||
|
||||
switch config.Type {
|
||||
case types.DatabaseCA, types.SAMLIDPCA, types.OIDCIdPCA:
|
||||
// These CAs only support RSA.
|
||||
keyPEM = fixtures.PEMBytes["rsa"]
|
||||
case types.DatabaseClientCA:
|
||||
// The db client CA also only supports RSA, but some tests rely on it
|
||||
// being different than the DB CA.
|
||||
keyPEM = fixtures.PEMBytes["rsa-db-client"]
|
||||
}
|
||||
if len(config.PrivateKeys) > 0 {
|
||||
// Allow test to override the private key.
|
||||
keyPEM = config.PrivateKeys[0]
|
||||
}
|
||||
|
||||
if keyPEM != nil {
|
||||
var err error
|
||||
key, err = keys.ParsePrivateKey(keyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
// If config.PrivateKeys was not set and this CA does not exclusively
|
||||
// support RSA, generate an ECDSA key. Signatures are ~10x faster than
|
||||
// RSA and generating a new key is actually faster than parsing a PEM
|
||||
// fixture.
|
||||
signer, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
key, err = keys.NewPrivateKey(signer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyPEM = key.PrivateKeyPEM()
|
||||
}
|
||||
|
||||
ca := &types.CertAuthorityV2{
|
||||
Kind: types.KindCertAuthority,
|
||||
SubKind: string(config.Type),
|
||||
Version: types.V2,
|
||||
Metadata: types.Metadata{
|
||||
Name: config.ResourceName,
|
||||
Namespace: apidefaults.Namespace,
|
||||
},
|
||||
Spec: types.CertAuthoritySpecV2{
|
||||
Type: config.Type,
|
||||
ClusterName: config.ClusterName,
|
||||
},
|
||||
}
|
||||
|
||||
// Add SSH keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.OpenSSHCA:
|
||||
ca.Spec.ActiveKeys.SSH = []*types.SSHKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: key.MarshalSSHPublicKey(),
|
||||
}}
|
||||
}
|
||||
|
||||
// Add TLS keys if necessary.
|
||||
switch config.Type {
|
||||
case types.UserCA, types.HostCA, types.DatabaseCA, types.DatabaseClientCA, types.SAMLIDPCA, types.SPIFFECA, types.AWSRACA:
|
||||
cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
|
||||
Signer: key.Signer,
|
||||
Entity: pkix.Name{
|
||||
CommonName: config.ClusterName,
|
||||
Organization: []string{config.SubjectOrganization},
|
||||
},
|
||||
TTL: defaults.CATTL,
|
||||
Clock: config.Clock,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.TLS = []*types.TLSKeyPair{{
|
||||
Key: keyPEM,
|
||||
Cert: cert,
|
||||
}}
|
||||
}
|
||||
|
||||
// Add JWT keys if necessary.
|
||||
switch config.Type {
|
||||
case types.JWTSigner, types.OIDCIdPCA, types.SPIFFECA, types.OktaCA, types.BoundKeypairCA:
|
||||
pubKeyPEM, err := keys.MarshalPublicKey(key.Public())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ca.Spec.ActiveKeys.JWT = []*types.JWTKeyPair{{
|
||||
PrivateKey: keyPEM,
|
||||
PublicKey: pubKeyPEM,
|
||||
}}
|
||||
}
|
||||
|
||||
return ca
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user