Skip to content
Merged
Show file tree
Hide file tree
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
25 changes: 25 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"google.golang.org/grpc/credentials"
ggzip "google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/keepalive"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"

Expand Down Expand Up @@ -4317,6 +4318,30 @@ func (c *Client) UpsertCertAuthority(ctx context.Context, ca types.CertAuthority
return out, trace.Wrap(err)
}

// RotateCertAuthority updates or inserts new cert authority
func (c *Client) RotateCertAuthority(ctx context.Context, rr types.RotateRequest) error {
req := &trustpb.RotateCertAuthorityRequest{
Type: string(rr.Type),
TargetPhase: rr.TargetPhase,
Mode: rr.Mode,
}

if rr.GracePeriod != nil {
req.GracePeriod = durationpb.New(*rr.GracePeriod)
}

if rr.Schedule != nil {
req.Schedule = &trustpb.RotationSchedule{
UpdateClients: timestamppb.New(rr.Schedule.UpdateClients),
UpdateServers: timestamppb.New(rr.Schedule.UpdateServers),
Standby: timestamppb.New(rr.Schedule.Standby),
}
}

_, err := c.TrustClient().RotateCertAuthority(ctx, req)
return trace.Wrap(err)
}

// UpdateHeadlessAuthenticationState updates a headless authentication state.
func (c *Client) UpdateHeadlessAuthenticationState(ctx context.Context, id string, state types.HeadlessAuthenticationState, mfaResponse *proto.MFAAuthenticateResponse) error {
_, err := c.grpc.UpdateHeadlessAuthenticationState(ctx, &proto.UpdateHeadlessAuthenticationStateRequest{
Expand Down
508 changes: 398 additions & 110 deletions api/gen/proto/go/teleport/trust/v1/trust_service.pb.go

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions api/proto/teleport/trust/v1/trust_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package teleport.trust.v1;

import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "teleport/legacy/types/types.proto";

option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1;trustv1";
Expand All @@ -32,6 +33,8 @@ service TrustService {
rpc DeleteCertAuthority(DeleteCertAuthorityRequest) returns (google.protobuf.Empty);
// UpsertCertAuthority creates or updates the provided cert authority.
rpc UpsertCertAuthority(UpsertCertAuthorityRequest) returns (types.CertAuthorityV2);
// RotateCertAuthority is a request to start rotation of the certificate authority.
rpc RotateCertAuthority(RotateCertAuthorityRequest) returns (RotateCertAuthorityResponse);
// GenerateHostCert takes a public key in the OpenSSH `authorized_keys` format and returns
// a SSH certificate signed by the Host CA.
rpc GenerateHostCert(GenerateHostCertRequest) returns (GenerateHostCertResponse);
Expand Down Expand Up @@ -75,6 +78,43 @@ message UpsertCertAuthorityRequest {
types.CertAuthorityV2 cert_authority = 1;
}

// Request for RotateCertAuthority.
message RotateCertAuthorityRequest {
// Type is a certificate authority type, if omitted, both user and host CA
// will be rotated.
string type = 1;
// GracePeriod is used to generate cert rotation schedule that defines
// times at which different rotation phases will be applied by the auth server
// in auto mode. It is not used in manual rotation mode.
// If omitted, default value is set, if 0 is supplied, it is interpreted as
// forcing rotation of all certificate authorities with no grace period,
// all existing users and hosts will have to re-login and re-added
// into the cluster.
google.protobuf.Duration grace_period = 2;
// TargetPhase sets desired rotation phase to move to, if not set
// will be set automatically, it is a required argument
// for manual rotation.
string target_phase = 3;
// Mode sets manual or auto rotation mode.
string mode = 4;
// Schedule is an optional rotation schedule,
// autogenerated based on GracePeriod parameter if not set.
RotationSchedule schedule = 5;
}

// RotationSchedule is a rotation schedule setting time switches for different phases.
message RotationSchedule {
// UpdateClients specifies time to switch to the "Update clients" phase
google.protobuf.Timestamp update_clients = 1;
// UpdateServers specifies time to switch to the "Update servers" phase.
google.protobuf.Timestamp update_servers = 2;
// Standby specifies time to switch to the "Standby" phase.
google.protobuf.Timestamp standby = 3;
}

// Response for RotateCertAuthority.
message RotateCertAuthorityResponse {}

// GenerateHostCertRequest is the request for GenerateHostCert.
message GenerateHostCertRequest {
// key is the SSH public key that the certificate should include.
Expand Down
59 changes: 59 additions & 0 deletions api/types/trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ package types
import (
"fmt"
"strings"
"time"

"github.com/coreos/go-semver/semver"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"

"github.com/gravitational/teleport/api"
"github.com/gravitational/teleport/api/defaults"
)

// CertAuthType specifies certificate authority type. New variants should be
Expand Down Expand Up @@ -107,3 +110,59 @@ func (c *CertAuthID) Check() error {
}
return nil
}

type RotateRequest struct {
// Type is a certificate authority type, if omitted, both user and host CA
// will be rotated.
Type CertAuthType `json:"type"`
// GracePeriod is used to generate cert rotation schedule that defines
// times at which different rotation phases will be applied by the auth server
// in auto mode. It is not used in manual rotation mode.
// If omitted, default value is set, if 0 is supplied, it is interpreted as
// forcing rotation of all certificate authorities with no grace period,
// all existing users and hosts will have to re-login and re-added
// into the cluster.
GracePeriod *time.Duration `json:"grace_period,omitempty"`
// TargetPhase sets desired rotation phase to move to, if not set
// will be set automatically, it is a required argument
// for manual rotation.
TargetPhase string `json:"target_phase,omitempty"`
// Mode sets manual or auto rotation mode.
Mode string `json:"mode"`
// Schedule is an optional rotation schedule,
// autogenerated based on GracePeriod parameter if not set.
Schedule *RotationSchedule `json:"schedule"`
}

// CheckAndSetDefaults checks and sets default values.
func (r *RotateRequest) CheckAndSetDefaults(clock clockwork.Clock) error {
if r.TargetPhase == "" {
// if phase is not set, imply that the first meaningful phase
// is set as a target phase
r.TargetPhase = RotationPhaseInit
}
// if mode is not set, default to manual (as it's safer)
if r.Mode == "" {
r.Mode = RotationModeManual
}

if err := r.Type.Check(); err != nil {
return trace.Wrap(err)
}
if r.GracePeriod == nil {
period := defaults.MaxCertDuration
r.GracePeriod = &period
}
if r.Schedule == nil {
var err error
r.Schedule, err = GenerateSchedule(clock.Now(), *r.GracePeriod)
if err != nil {
return trace.Wrap(err)
}
} else {
if err := r.Schedule.CheckAndSetDefaults(clock.Now()); err != nil {
return trace.Wrap(err)
}
}
return nil
}
2 changes: 1 addition & 1 deletion integration/db/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ func (p *DatabasePack) testRotateTrustedCluster(t *testing.T) {

go func() {
errChan <- pw.waitForPhase(phase, func() error {
return authServer.RotateCertAuthority(ctx, auth.RotateRequest{
return authServer.RotateCertAuthority(ctx, types.RotateRequest{
Type: types.DatabaseCA,
TargetPhase: phase,
Mode: types.RotationModeManual,
Expand Down
18 changes: 9 additions & 9 deletions integration/hsm/hsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func TestHSMRotation(t *testing.T) {
allServices = append(allServices, proxy)

log.Debug("TestHSMRotation: sending rotation request init")
err := auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
err := auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: types.RotationPhaseInit,
Mode: types.RotationModeManual,
Expand All @@ -166,7 +166,7 @@ func TestHSMRotation(t *testing.T) {
require.NoError(t, allServices.waitForPhaseChange(ctx))

log.Debug("TestHSMRotation: sending rotation request update_clients")
err = auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
err = auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: types.RotationPhaseUpdateClients,
Mode: types.RotationModeManual,
Expand All @@ -175,7 +175,7 @@ func TestHSMRotation(t *testing.T) {
require.NoError(t, allServices.waitForRestart(ctx))

log.Debug("TestHSMRotation: sending rotation request update_servers")
err = auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
err = auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: types.RotationPhaseUpdateServers,
Mode: types.RotationModeManual,
Expand All @@ -184,7 +184,7 @@ func TestHSMRotation(t *testing.T) {
require.NoError(t, allServices.waitForRestart(ctx))

log.Debug("TestHSMRotation: sending rotation request standby")
err = auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
err = auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: types.RotationPhaseStandby,
Mode: types.RotationModeManual,
Expand Down Expand Up @@ -334,7 +334,7 @@ func TestHSMDualAuthRotation(t *testing.T) {
// do a full rotation
for _, stage := range stages {
log.Debugf("TestHSMDualAuthRotation: Sending rotate request %s", stage.targetPhase)
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: stage.targetPhase,
Mode: types.RotationModeManual,
Expand All @@ -353,7 +353,7 @@ func TestHSMDualAuthRotation(t *testing.T) {
// Do another full rotation from the new auth server
for _, stage := range stages {
log.Debugf("TestHSMDualAuthRotation: Sending rotate request %s", stage.targetPhase)
require.NoError(t, auth2.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
require.NoError(t, auth2.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: stage.targetPhase,
Mode: types.RotationModeManual,
Expand Down Expand Up @@ -456,7 +456,7 @@ func TestHSMDualAuthRotation(t *testing.T) {
}
for _, stage := range stages {
log.Debugf("TestHSMDualAuthRotation: Sending rotate request %s", stage.targetPhase)
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: stage.targetPhase,
Mode: types.RotationModeManual,
Expand Down Expand Up @@ -563,7 +563,7 @@ func TestHSMMigrate(t *testing.T) {
// do a full rotation
for _, stage := range stages {
log.Debugf("TestHSMMigrate: Sending rotate request %s", stage.targetPhase)
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: stage.targetPhase,
Mode: types.RotationModeManual,
Expand All @@ -590,7 +590,7 @@ func TestHSMMigrate(t *testing.T) {
// do a full rotation
for _, stage := range stages {
log.Debugf("TestHSMMigrate: Sending rotate request %s", stage.targetPhase)
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, auth.RotateRequest{
require.NoError(t, auth1.process.GetAuthServer().RotateCertAuthority(ctx, types.RotateRequest{
Type: types.HostCA,
TargetPhase: stage.targetPhase,
Mode: types.RotationModeManual,
Expand Down
Loading