mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 17:23:22 +00:00
Remove deprecated cert authority fields and migration (#16761)
* Remove old/deprecated cert authority migration * Remove on-the-fly migration and deprecated fields * Remove references to removed and deprecated key fields * Fix tests for removed fields * Fix tests that referenced deprecated fields
This commit is contained in:
parent
77f8a4ef10
commit
85c426bbcd
|
@ -792,6 +792,7 @@ message CertAuthorityV2 {
|
|||
// CertAuthoritySpecV2 is a host or user certificate authority that
|
||||
// can check and if it has private key stored as well, sign it too
|
||||
message CertAuthoritySpecV2 {
|
||||
reserved 3, 4, 7, 10;
|
||||
// Type is either user or host certificate authority
|
||||
string Type = 1 [
|
||||
(gogoproto.jsontag) = "type",
|
||||
|
@ -804,21 +805,6 @@ message CertAuthoritySpecV2 {
|
|||
// for host authorities that means base hostname of all servers,
|
||||
// for user authorities that means organization name
|
||||
string ClusterName = 2 [(gogoproto.jsontag) = "cluster_name"];
|
||||
// Checkers is a list of SSH public keys that can be used to check
|
||||
// certificate signatures
|
||||
//
|
||||
// DEPRECATED: use ActiveKeys and AdditionalTrustedKeys instead.
|
||||
repeated bytes CheckingKeys = 3 [
|
||||
(gogoproto.jsontag) = "checking_keys,omitempty",
|
||||
deprecated = true
|
||||
];
|
||||
// SigningKeys is a list of private keys used for signing
|
||||
//
|
||||
// DEPRECATED: use ActiveKeys instead.
|
||||
repeated bytes SigningKeys = 4 [
|
||||
(gogoproto.jsontag) = "signing_keys,omitempty",
|
||||
deprecated = true
|
||||
];
|
||||
// Roles is a list of roles assumed by users signed by this CA
|
||||
repeated string Roles = 5 [(gogoproto.jsontag) = "roles,omitempty"];
|
||||
// RoleMap specifies role mappings to remote roles
|
||||
|
@ -826,14 +812,6 @@ message CertAuthoritySpecV2 {
|
|||
(gogoproto.nullable) = false,
|
||||
(gogoproto.jsontag) = "role_map,omitempty"
|
||||
];
|
||||
// TLS is a list of TLS key pairs
|
||||
//
|
||||
// DEPRECATED: use ActiveKeys and AdditionalTrustedKeys instead.
|
||||
repeated TLSKeyPair TLSKeyPairs = 7 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.jsontag) = "tls_key_pairs,omitempty",
|
||||
deprecated = true
|
||||
];
|
||||
// Rotation is a status of the certificate authority rotation
|
||||
Rotation Rotation = 8 [
|
||||
(gogoproto.nullable) = true,
|
||||
|
@ -848,15 +826,6 @@ message CertAuthoritySpecV2 {
|
|||
RSA_SHA2_512 = 3;
|
||||
}
|
||||
SigningAlgType SigningAlg = 9 [(gogoproto.jsontag) = "signing_alg,omitempty"];
|
||||
// JWTKeyPair is a list of JWT key pairs.
|
||||
//
|
||||
// DEPRECATED: use ActiveKeys and AdditionalTrustedKeys instead.
|
||||
repeated JWTKeyPair JWTKeyPairs = 10 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.jsontag) = "jwt_key_pairs,omitempty",
|
||||
deprecated = true
|
||||
];
|
||||
|
||||
// ActiveKeys are the CA key sets used to sign any new certificates.
|
||||
CAKeySet ActiveKeys = 11 [
|
||||
(gogoproto.nullable) = false,
|
||||
|
|
|
@ -168,16 +168,6 @@ func RemoveCASecrets(ca CertAuthority) {
|
|||
if !ok {
|
||||
return
|
||||
}
|
||||
cav2.Spec.SigningKeys = nil
|
||||
|
||||
for i := range cav2.Spec.TLSKeyPairs {
|
||||
cav2.Spec.TLSKeyPairs[i].Key = nil
|
||||
}
|
||||
|
||||
for i := range cav2.Spec.JWTKeyPairs {
|
||||
cav2.Spec.JWTKeyPairs[i].PrivateKey = nil
|
||||
}
|
||||
|
||||
cav2.Spec.ActiveKeys = cav2.Spec.ActiveKeys.WithoutSecrets()
|
||||
cav2.Spec.AdditionalTrustedKeys = cav2.Spec.AdditionalTrustedKeys.WithoutSecrets()
|
||||
}
|
||||
|
@ -259,38 +249,8 @@ func (ca *CertAuthorityV2) ID() *CertAuthID {
|
|||
return &CertAuthID{DomainName: ca.Spec.ClusterName, Type: ca.Spec.Type}
|
||||
}
|
||||
|
||||
func (ca *CertAuthorityV2) getOldKeySet(index int) (keySet CAKeySet) {
|
||||
// in the "old" CA schema, index 0 contains the active keys and index 1 the
|
||||
// additional trusted keys
|
||||
if index < 0 || index > 1 {
|
||||
return
|
||||
}
|
||||
if len(ca.Spec.CheckingKeys) > index {
|
||||
kp := &SSHKeyPair{
|
||||
PrivateKeyType: PrivateKeyType_RAW,
|
||||
PublicKey: utils.CopyByteSlice(ca.Spec.CheckingKeys[index]),
|
||||
}
|
||||
if len(ca.Spec.SigningKeys) > index {
|
||||
kp.PrivateKey = utils.CopyByteSlice(ca.Spec.SigningKeys[index])
|
||||
}
|
||||
keySet.SSH = []*SSHKeyPair{kp}
|
||||
}
|
||||
if len(ca.Spec.TLSKeyPairs) > index {
|
||||
keySet.TLS = []*TLSKeyPair{ca.Spec.TLSKeyPairs[index].Clone()}
|
||||
}
|
||||
if len(ca.Spec.JWTKeyPairs) > index {
|
||||
keySet.JWT = []*JWTKeyPair{ca.Spec.JWTKeyPairs[index].Clone()}
|
||||
}
|
||||
return keySet
|
||||
}
|
||||
|
||||
func (ca *CertAuthorityV2) GetActiveKeys() CAKeySet {
|
||||
haveNewCAKeys := len(ca.Spec.ActiveKeys.SSH) > 0 || len(ca.Spec.ActiveKeys.TLS) > 0 || len(ca.Spec.ActiveKeys.JWT) > 0
|
||||
if haveNewCAKeys {
|
||||
return ca.Spec.ActiveKeys
|
||||
}
|
||||
// fall back to old schema
|
||||
return ca.getOldKeySet(0)
|
||||
return ca.Spec.ActiveKeys
|
||||
}
|
||||
|
||||
func (ca *CertAuthorityV2) SetActiveKeys(ks CAKeySet) error {
|
||||
|
@ -302,12 +262,7 @@ func (ca *CertAuthorityV2) SetActiveKeys(ks CAKeySet) error {
|
|||
}
|
||||
|
||||
func (ca *CertAuthorityV2) GetAdditionalTrustedKeys() CAKeySet {
|
||||
haveNewCAKeys := len(ca.Spec.AdditionalTrustedKeys.SSH) > 0 || len(ca.Spec.AdditionalTrustedKeys.TLS) > 0 || len(ca.Spec.AdditionalTrustedKeys.JWT) > 0
|
||||
if haveNewCAKeys {
|
||||
return ca.Spec.AdditionalTrustedKeys
|
||||
}
|
||||
// fall back to old schema
|
||||
return ca.getOldKeySet(1)
|
||||
return ca.Spec.AdditionalTrustedKeys
|
||||
}
|
||||
|
||||
func (ca *CertAuthorityV2) SetAdditionalTrustedKeys(ks CAKeySet) error {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -521,9 +521,6 @@ func migrateLegacyResources(ctx context.Context, asrv *Server) error {
|
|||
if err := migrateRemoteClusters(ctx, asrv); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if err := migrateCertAuthorities(ctx, asrv); err != nil {
|
||||
return trace.Wrap(err, "fail to migrate certificate authorities to the v7 storage format: %v; please report this at https://github.com/gravitational/teleport/issues/new?assignees=&labels=bug&template=bug_report.md including the *redacted* output of 'tctl get cert_authority'", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1042,35 +1039,6 @@ func migrateRemoteClusters(ctx context.Context, asrv *Server) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DELETE IN: 8.0.0
|
||||
// migrateCertAuthorities migrates the keypair storage format in cert
|
||||
// authorities to the new format.
|
||||
func migrateCertAuthorities(ctx context.Context, asrv *Server) error {
|
||||
var errors []error
|
||||
for _, caType := range []types.CertAuthType{types.HostCA, types.UserCA, types.JWTSigner} {
|
||||
cas, err := asrv.Services.GetCertAuthorities(ctx, caType, true)
|
||||
if err != nil {
|
||||
errors = append(errors, trace.Wrap(err, "fetching %v CAs", caType))
|
||||
continue
|
||||
}
|
||||
for _, ca := range cas {
|
||||
if err := migrateCertAuthority(asrv, ca); err != nil {
|
||||
errors = append(errors, trace.Wrap(err, "failed to migrate %v: %v", ca, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
log.Errorf("Failed to migrate certificate authorities to the v7 storage format.")
|
||||
log.Errorf("Please report the *exact* errors below and *redacted* output of 'tctl get cert_authority' at https://github.com/gravitational/teleport/issues/new?assignees=&labels=bug&template=bug_report.md")
|
||||
for _, err := range errors {
|
||||
log.Errorf(" %v", err)
|
||||
}
|
||||
return trace.Errorf("fail to migrate certificate authorities to the v7 storage format")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateDBAuthority copies Host CA as Database CA. Before v9.0 database access was using host CA to sign all
|
||||
// DB certificates. In order to support existing installations Teleport copies Host CA as Database CA on
|
||||
// the first run after update to v9.0+.
|
||||
|
@ -1151,30 +1119,3 @@ func migrateDBAuthority(ctx context.Context, asrv *Server) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateCertAuthority(asrv *Server, ca types.CertAuthority) error {
|
||||
// Check if we need to migrate.
|
||||
if needsMigration, err := services.CertAuthorityNeedsMigration(ca); err != nil || !needsMigration {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
log.Infof("Migrating %v to 7.0 storage format.", ca)
|
||||
// CA rotation can cause weird edge cases during migration, don't allow
|
||||
// rotation and migration in parallel.
|
||||
if ca.GetRotation().State == types.RotationStateInProgress {
|
||||
return trace.BadParameter("CA rotation is in progress; please finish CA rotation before upgrading teleport")
|
||||
}
|
||||
|
||||
if err := services.SyncCertAuthorityKeys(ca); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Sanity-check and upsert the modified CA.
|
||||
if err := services.ValidateCertAuthority(ca); err != nil {
|
||||
return trace.Wrap(err, "the migrated CA is invalid: %v", err)
|
||||
}
|
||||
if err := asrv.UpsertCertAuthority(ca); err != nil {
|
||||
return trace.Wrap(err, "failed storing the migrated CA: %v", err)
|
||||
}
|
||||
log.Infof("Successfully migrated %v to 7.0 storage format.", ca)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package auth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -33,7 +32,6 @@ import (
|
|||
"github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/backend/lite"
|
||||
"github.com/gravitational/teleport/lib/fixtures"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/services/suite"
|
||||
"github.com/gravitational/teleport/lib/sshutils"
|
||||
|
@ -626,108 +624,6 @@ func newU2FAuthPreferenceFromConfigFile(t *testing.T) types.AuthPreference {
|
|||
return ap
|
||||
}
|
||||
|
||||
func TestMigrateCertAuthorities(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
as := newTestAuthServer(ctx, t)
|
||||
clock := clockwork.NewFakeClock()
|
||||
as.SetClock(clock)
|
||||
|
||||
for _, spec := range []types.CertAuthoritySpecV2{
|
||||
{
|
||||
Type: types.HostCA,
|
||||
ClusterName: "localhost",
|
||||
CheckingKeys: [][]byte{[]byte(fixtures.SSHCAPublicKey)},
|
||||
SigningKeys: [][]byte{[]byte(fixtures.SSHCAPrivateKey)},
|
||||
TLSKeyPairs: []types.TLSKeyPair{{Cert: []byte(fixtures.TLSCACertPEM), Key: []byte(fixtures.TLSCAKeyPEM)}},
|
||||
Rotation: nil, // Rotation was never performed.
|
||||
},
|
||||
{
|
||||
Type: types.UserCA,
|
||||
ClusterName: "localhost",
|
||||
CheckingKeys: [][]byte{[]byte(fixtures.SSHCAPublicKey)},
|
||||
SigningKeys: [][]byte{[]byte(fixtures.SSHCAPrivateKey)},
|
||||
TLSKeyPairs: []types.TLSKeyPair{{Cert: []byte(fixtures.TLSCACertPEM), Key: []byte(fixtures.TLSCAKeyPEM)}},
|
||||
Rotation: &types.Rotation{State: types.RotationStateStandby},
|
||||
},
|
||||
{
|
||||
Type: types.JWTSigner,
|
||||
ClusterName: "localhost",
|
||||
JWTKeyPairs: []types.JWTKeyPair{{PublicKey: []byte(fixtures.JWTSignerPublicKey), PrivateKey: []byte(fixtures.JWTSignerPrivateKey)}},
|
||||
Rotation: &types.Rotation{State: types.RotationStateStandby},
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("create %v CA", spec.Type), func(t *testing.T) {
|
||||
ca, err := types.NewCertAuthority(spec)
|
||||
require.NoError(t, err)
|
||||
// Do NOT use services.MarshalCertAuthority to keep all fields as-is.
|
||||
enc, err := utils.FastMarshal(ca)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = as.bk.Put(ctx, backend.Item{
|
||||
Key: backend.Key("authorities", string(ca.GetType()), ca.GetName()),
|
||||
Value: enc,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
err := migrateCertAuthorities(ctx, as)
|
||||
require.NoError(t, err)
|
||||
|
||||
var caSpecs []types.CertAuthoritySpecV2
|
||||
for _, typ := range []types.CertAuthType{types.HostCA, types.UserCA, types.JWTSigner} {
|
||||
t.Run(fmt.Sprintf("verify %v CA", typ), func(t *testing.T) {
|
||||
cas, err := as.GetCertAuthorities(ctx, typ, true)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cas, 1)
|
||||
caSpecs = append(caSpecs, cas[0].(*types.CertAuthorityV2).Spec)
|
||||
})
|
||||
}
|
||||
require.Empty(t, cmp.Diff(caSpecs, []types.CertAuthoritySpecV2{
|
||||
{
|
||||
Type: types.HostCA,
|
||||
ClusterName: "localhost",
|
||||
ActiveKeys: types.CAKeySet{
|
||||
SSH: []*types.SSHKeyPair{{
|
||||
PrivateKey: []byte(fixtures.SSHCAPrivateKey),
|
||||
PublicKey: []byte(fixtures.SSHCAPublicKey),
|
||||
}},
|
||||
TLS: []*types.TLSKeyPair{{Cert: []byte(fixtures.TLSCACertPEM), Key: []byte(fixtures.TLSCAKeyPEM)}},
|
||||
},
|
||||
CheckingKeys: [][]byte{[]byte(fixtures.SSHCAPublicKey)},
|
||||
SigningKeys: [][]byte{[]byte(fixtures.SSHCAPrivateKey)},
|
||||
TLSKeyPairs: []types.TLSKeyPair{{Cert: []byte(fixtures.TLSCACertPEM), Key: []byte(fixtures.TLSCAKeyPEM)}},
|
||||
Rotation: nil,
|
||||
},
|
||||
{
|
||||
Type: types.UserCA,
|
||||
ClusterName: "localhost",
|
||||
ActiveKeys: types.CAKeySet{
|
||||
SSH: []*types.SSHKeyPair{{
|
||||
PrivateKey: []byte(fixtures.SSHCAPrivateKey),
|
||||
PublicKey: []byte(fixtures.SSHCAPublicKey),
|
||||
}},
|
||||
TLS: []*types.TLSKeyPair{{Cert: []byte(fixtures.TLSCACertPEM), Key: []byte(fixtures.TLSCAKeyPEM)}},
|
||||
},
|
||||
CheckingKeys: [][]byte{[]byte(fixtures.SSHCAPublicKey)},
|
||||
SigningKeys: [][]byte{[]byte(fixtures.SSHCAPrivateKey)},
|
||||
TLSKeyPairs: []types.TLSKeyPair{{Cert: []byte(fixtures.TLSCACertPEM), Key: []byte(fixtures.TLSCAKeyPEM)}},
|
||||
Rotation: &types.Rotation{State: types.RotationStateStandby},
|
||||
},
|
||||
{
|
||||
Type: types.JWTSigner,
|
||||
ClusterName: "localhost",
|
||||
ActiveKeys: types.CAKeySet{
|
||||
JWT: []*types.JWTKeyPair{{PublicKey: []byte(fixtures.JWTSignerPublicKey), PrivateKey: []byte(fixtures.JWTSignerPrivateKey)}},
|
||||
},
|
||||
JWTKeyPairs: []types.JWTKeyPair{{PublicKey: []byte(fixtures.JWTSignerPublicKey), PrivateKey: []byte(fixtures.JWTSignerPrivateKey)}},
|
||||
Rotation: &types.Rotation{State: types.RotationStateStandby},
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// Example resources generated using `tctl get all --with-secrets`.
|
||||
const (
|
||||
hostCAYAML = `kind: cert_authority
|
||||
|
@ -828,7 +724,6 @@ func TestInit_bootstrap(t *testing.T) {
|
|||
invalidUserCA.(*types.CertAuthorityV2).Spec.ActiveKeys.SSH = nil
|
||||
invalidJWTCA := resourceFromYAML(t, jwtCAYAML).(types.CertAuthority)
|
||||
invalidJWTCA.(*types.CertAuthorityV2).Spec.ActiveKeys.JWT = nil
|
||||
invalidJWTCA.(*types.CertAuthorityV2).Spec.JWTKeyPairs = nil
|
||||
invalidDBCA := resourceFromYAML(t, databaseCAYAML).(types.CertAuthority)
|
||||
invalidDBCA.(*types.CertAuthorityV2).Spec.ActiveKeys.TLS = nil
|
||||
|
||||
|
|
|
@ -106,9 +106,15 @@ type fakeClient struct {
|
|||
|
||||
func (fc *fakeClient) GetCertAuthorities(ctx context.Context, caType types.CertAuthType, loadKeys bool, opts ...services.MarshalOption) ([]types.CertAuthority, error) {
|
||||
ca, err := types.NewCertAuthority(types.CertAuthoritySpecV2{
|
||||
Type: types.HostCA,
|
||||
ClusterName: "example.com",
|
||||
CheckingKeys: [][]byte{ssh.MarshalAuthorizedKey(fc.caKey)},
|
||||
Type: types.HostCA,
|
||||
ClusterName: "example.com",
|
||||
ActiveKeys: types.CAKeySet{
|
||||
SSH: []*types.SSHKeyPair{
|
||||
{
|
||||
PublicKey: ssh.MarshalAuthorizedKey(fc.caKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
|
|
@ -70,10 +70,10 @@ func checkUserOrHostCA(cai types.CertAuthority) error {
|
|||
if !ok {
|
||||
return trace.BadParameter("unknown CA type %T", cai)
|
||||
}
|
||||
if len(ca.Spec.ActiveKeys.SSH) == 0 && len(ca.Spec.CheckingKeys) == 0 {
|
||||
if len(ca.Spec.ActiveKeys.SSH) == 0 {
|
||||
return trace.BadParameter("certificate authority missing SSH key pairs")
|
||||
}
|
||||
if len(ca.Spec.ActiveKeys.TLS) == 0 && len(ca.Spec.TLSKeyPairs) == 0 {
|
||||
if len(ca.Spec.ActiveKeys.TLS) == 0 {
|
||||
return trace.BadParameter("certificate authority missing TLS key pairs")
|
||||
}
|
||||
if _, err := sshutils.GetCheckers(ca); err != nil {
|
||||
|
@ -98,7 +98,7 @@ func checkDatabaseCA(cai types.CertAuthority) error {
|
|||
return trace.BadParameter("unknown CA type %T", cai)
|
||||
}
|
||||
|
||||
if len(ca.Spec.ActiveKeys.TLS) == 0 && len(ca.Spec.TLSKeyPairs) == 0 {
|
||||
if len(ca.Spec.ActiveKeys.TLS) == 0 {
|
||||
return trace.BadParameter("DB certificate authority missing TLS key pairs")
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ func checkJWTKeys(cai types.CertAuthority) error {
|
|||
return trace.BadParameter("unknown CA type %T", cai)
|
||||
}
|
||||
// Check that some JWT keys have been set on the CA.
|
||||
if len(ca.Spec.ActiveKeys.JWT) == 0 && len(ca.Spec.JWTKeyPairs) == 0 {
|
||||
if len(ca.Spec.ActiveKeys.JWT) == 0 {
|
||||
return trace.BadParameter("missing JWT CA")
|
||||
}
|
||||
|
||||
|
@ -440,96 +440,8 @@ func MarshalCertAuthority(certAuthority types.CertAuthority, opts ...MarshalOpti
|
|||
copy.SetResourceID(0)
|
||||
certAuthority = ©
|
||||
}
|
||||
if err := SyncCertAuthorityKeys(certAuthority); err != nil {
|
||||
return nil, trace.Wrap(err, "failed to sync CertAuthority key formats for %v: %v", certAuthority, err)
|
||||
}
|
||||
return utils.FastMarshal(certAuthority)
|
||||
default:
|
||||
return nil, trace.BadParameter("unrecognized certificate authority version %T", certAuthority)
|
||||
}
|
||||
}
|
||||
|
||||
// CertAuthorityNeedsMigration returns true if the given CertAuthority needs to be migrated
|
||||
func CertAuthorityNeedsMigration(cai types.CertAuthority) (bool, error) {
|
||||
ca, ok := cai.(*types.CertAuthorityV2)
|
||||
if !ok {
|
||||
return false, trace.BadParameter("unknown type %T", cai)
|
||||
}
|
||||
haveOldCAKeys := len(ca.Spec.CheckingKeys) > 0 || len(ca.Spec.TLSKeyPairs) > 0 || len(ca.Spec.JWTKeyPairs) > 0
|
||||
haveNewCAKeys := len(ca.Spec.ActiveKeys.SSH) > 0 || len(ca.Spec.ActiveKeys.TLS) > 0 || len(ca.Spec.ActiveKeys.JWT) > 0
|
||||
return haveOldCAKeys && !haveNewCAKeys, nil
|
||||
}
|
||||
|
||||
// SyncCertAuthorityKeys backfills the old or new key formats, if one of them
|
||||
// is empty. If both formats are present, SyncCertAuthorityKeys does nothing.
|
||||
func SyncCertAuthorityKeys(cai types.CertAuthority) error {
|
||||
ca, ok := cai.(*types.CertAuthorityV2)
|
||||
if !ok {
|
||||
return trace.BadParameter("unknown type %T", cai)
|
||||
}
|
||||
haveOldCAKeys := len(ca.Spec.CheckingKeys) > 0 || len(ca.Spec.TLSKeyPairs) > 0 || len(ca.Spec.JWTKeyPairs) > 0
|
||||
haveNewCAKeys := len(ca.Spec.ActiveKeys.SSH) > 0 || len(ca.Spec.ActiveKeys.TLS) > 0 || len(ca.Spec.ActiveKeys.JWT) > 0
|
||||
switch {
|
||||
case haveOldCAKeys && !haveNewCAKeys:
|
||||
return trace.Wrap(fillNewCertAuthorityKeys(ca))
|
||||
case !haveOldCAKeys && haveNewCAKeys:
|
||||
return trace.Wrap(fillOldCertAuthorityKeys(ca))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fillNewCertAuthorityKeys(ca *types.CertAuthorityV2) error {
|
||||
// Reset any old state.
|
||||
ca.Spec.ActiveKeys = types.CAKeySet{}
|
||||
ca.Spec.AdditionalTrustedKeys = types.CAKeySet{}
|
||||
|
||||
// Convert all the keypair fields to new format.
|
||||
|
||||
// SigningKeys key may be missing in the CA from a remote cluster.
|
||||
if len(ca.Spec.SigningKeys) > 0 && len(ca.Spec.SigningKeys) != len(ca.Spec.CheckingKeys) {
|
||||
return trace.BadParameter("mis-matched SSH private (%d) and public (%d) key counts", len(ca.Spec.SigningKeys), len(ca.Spec.CheckingKeys))
|
||||
}
|
||||
for i := range ca.Spec.CheckingKeys {
|
||||
kp := &types.SSHKeyPair{
|
||||
PrivateKeyType: types.PrivateKeyType_RAW,
|
||||
PublicKey: apiutils.CopyByteSlice(ca.Spec.CheckingKeys[i]),
|
||||
}
|
||||
if len(ca.Spec.SigningKeys) > 0 {
|
||||
kp.PrivateKey = apiutils.CopyByteSlice(ca.Spec.SigningKeys[i])
|
||||
}
|
||||
ca.Spec.ActiveKeys.SSH = append(ca.Spec.ActiveKeys.SSH, kp)
|
||||
}
|
||||
for _, kp := range ca.Spec.TLSKeyPairs {
|
||||
ca.Spec.ActiveKeys.TLS = append(ca.Spec.ActiveKeys.TLS, kp.Clone())
|
||||
}
|
||||
for _, kp := range ca.Spec.JWTKeyPairs {
|
||||
ca.Spec.ActiveKeys.JWT = append(ca.Spec.ActiveKeys.JWT, kp.Clone())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fillOldCertAuthorityKeys(ca *types.CertAuthorityV2) error {
|
||||
// Reset any old state.
|
||||
ca.Spec.SigningKeys = nil
|
||||
ca.Spec.CheckingKeys = nil
|
||||
ca.Spec.TLSKeyPairs = nil
|
||||
ca.Spec.JWTKeyPairs = nil
|
||||
|
||||
// Convert all the keypair fields to new format.
|
||||
for _, ks := range []types.CAKeySet{ca.Spec.ActiveKeys, ca.Spec.AdditionalTrustedKeys} {
|
||||
for _, kp := range ks.SSH {
|
||||
ca.Spec.CheckingKeys = append(ca.Spec.CheckingKeys, apiutils.CopyByteSlice(kp.PublicKey))
|
||||
// PrivateKey may be empty.
|
||||
if len(kp.PrivateKey) > 0 {
|
||||
ca.Spec.SigningKeys = append(ca.Spec.SigningKeys, apiutils.CopyByteSlice(kp.PrivateKey))
|
||||
}
|
||||
}
|
||||
for _, kp := range ks.TLS {
|
||||
ca.Spec.TLSKeyPairs = append(ca.Spec.TLSKeyPairs, *kp.Clone())
|
||||
}
|
||||
for _, kp := range ks.JWT {
|
||||
ca.Spec.JWTKeyPairs = append(ca.Spec.JWTKeyPairs, *kp.Clone())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -69,19 +69,6 @@ func TestCertPoolFromCertAuthorities(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// CA for cluster3 with old schema
|
||||
key, cert, err = tlsca.GenerateSelfSignedCA(pkix.Name{CommonName: "cluster3"}, nil, time.Minute)
|
||||
require.NoError(t, err)
|
||||
ca3, err := types.NewCertAuthority(types.CertAuthoritySpecV2{
|
||||
Type: types.HostCA,
|
||||
ClusterName: "cluster3",
|
||||
TLSKeyPairs: []types.TLSKeyPair{{
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("ca1 with 1 cert", func(t *testing.T) {
|
||||
pool, count, err := CertPoolFromCertAuthorities([]types.CertAuthority{ca1})
|
||||
require.NotNil(t, pool)
|
||||
|
@ -94,18 +81,12 @@ func TestCertPoolFromCertAuthorities(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, 2, count)
|
||||
})
|
||||
t.Run("ca3 with 1 cert", func(t *testing.T) {
|
||||
pool, count, err := CertPoolFromCertAuthorities([]types.CertAuthority{ca3})
|
||||
require.NotNil(t, pool)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, count)
|
||||
})
|
||||
|
||||
t.Run("ca1 + ca2 + ca3 with 4 certs total", func(t *testing.T) {
|
||||
pool, count, err := CertPoolFromCertAuthorities([]types.CertAuthority{ca1, ca2, ca3})
|
||||
t.Run("ca1 + ca2 with 3 certs total", func(t *testing.T) {
|
||||
pool, count, err := CertPoolFromCertAuthorities([]types.CertAuthority{ca1, ca2})
|
||||
require.NotNil(t, pool)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 4, count)
|
||||
require.Equal(t, 3, count)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -187,9 +168,6 @@ func TestCertAuthorityUTCUnmarshal(t *testing.T) {
|
|||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// needed for CertAuthoritiesEquivalent, as this will get called by
|
||||
// UnmarshalCertAuthority
|
||||
require.NoError(t, SyncCertAuthorityKeys(caLocal))
|
||||
|
||||
_, offset := caLocal.GetRotation().LastRotated.Zone()
|
||||
require.NotZero(t, offset)
|
||||
|
|
|
@ -140,9 +140,6 @@ func NewTestCAWithConfig(config TestCAConfig) *types.CertAuthorityV2 {
|
|||
panic("unknown CA type")
|
||||
}
|
||||
|
||||
if err := services.SyncCertAuthorityKeys(ca); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ca
|
||||
}
|
||||
|
||||
|
@ -309,9 +306,7 @@ func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
ca2 := ca.Clone().(*types.CertAuthorityV2)
|
||||
ca2.Spec.ActiveKeys.SSH[0].PrivateKey = nil
|
||||
ca2.Spec.SigningKeys = nil
|
||||
ca2.Spec.ActiveKeys.TLS[0].Key = nil
|
||||
ca2.Spec.TLSKeyPairs[0].Key = nil
|
||||
require.Equal(t, cas[0], ca2)
|
||||
|
||||
cas, err = s.CAS.GetCertAuthorities(ctx, types.UserCA, true)
|
||||
|
|
Loading…
Reference in a new issue