Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve ca cmp #10351

Merged
merged 2 commits into from
Mar 23, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions lib/services/local/trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ package local

import (
"context"
"fmt"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/services"
log "github.com/sirupsen/logrus"

"github.com/gravitational/trace"
)
Expand Down Expand Up @@ -104,42 +102,42 @@ func (s *CA) UpsertCertAuthority(ca types.CertAuthority) error {
return nil
}

const compareAndSwapFixExample = "tctl get %s/%s/%s --with-secrets > ca.yaml && tctl create -f ca.yaml && rm ca.yaml"

// CompareAndSwapCertAuthority updates the cert authority value
// if the existing value matches existing parameter, returns nil if succeeds,
// if the existing value matches expected parameter, returns nil if succeeds,
// trace.CompareFailed otherwise.
func (s *CA) CompareAndSwapCertAuthority(new, existing types.CertAuthority) error {
func (s *CA) CompareAndSwapCertAuthority(new, expected types.CertAuthority) error {
if err := services.ValidateCertAuthority(new); err != nil {
return trace.Wrap(err)
}
newValue, err := services.MarshalCertAuthority(new)

key := backend.Key(authoritiesPrefix, string(new.GetType()), new.GetName())

actualItem, err := s.Get(context.TODO(), key)
if err != nil {
return trace.Wrap(err)
}
newItem := backend.Item{
Key: backend.Key(authoritiesPrefix, string(new.GetType()), new.GetName()),
Value: newValue,
Expires: new.Expiry(),
actual, err := services.UnmarshalCertAuthority(actualItem.Value)
if err != nil {
return trace.Wrap(err)
}

if !services.CertAuthoritiesEquivalent(actual, expected) {
return trace.CompareFailed("cluster %v settings have been updated, try again", new.GetName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that it's the exact same message as before, but this is only accurate if the rotation was indeed the only thing different between actual and expected. Would it be too much of a layer violation if we checked for that here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow. Are you suggesting we return a different error message based on which elements of the CAs differ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. check rotation state and return something like "rotation state concurrently updated"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and maybe leave the "try again" as that message is displayed verbatim by tctl on a manual auth - whereas if something bad has happened and we are never actually going to recover by waiting, there's no point in telling the user to try again.

Copy link
Contributor

@espadolini espadolini Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admittedly the fix could be to actually catch and replace the error on one of the layers between this and the user instead

}

existingValue, err := services.MarshalCertAuthority(existing)
newValue, err := services.MarshalCertAuthority(new)
if err != nil {
return trace.Wrap(err)
}
existingItem := backend.Item{
Key: backend.Key(authoritiesPrefix, string(existing.GetType()), existing.GetName()),
Value: existingValue,
Expires: existing.Expiry(),
newItem := backend.Item{
Key: key,
Value: newValue,
Expires: new.Expiry(),
}

_, err = s.CompareAndSwap(context.TODO(), existingItem, newItem)
_, err = s.CompareAndSwap(context.TODO(), *actualItem, newItem)
if err != nil {
if trace.IsCompareFailed(err) {
if len(existing.GetMetadata().Labels) >= 2 {
exampleCmd := fmt.Sprintf(compareAndSwapFixExample, existing.GetKind(), existing.GetSubKind(), existing.GetName())
log.Warnf("comparison failed on certificate authority with multiple labels; if this occurs consistently, try re-saving the resource: %s", exampleCmd)
}
return trace.CompareFailed("cluster %v settings have been updated, try again", new.GetName())
}
return trace.Wrap(err)
Expand Down