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

feat: interactive tctl auth rotate #49171

Merged
merged 2 commits into from
Dec 5, 2024
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
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ require (
github.com/buildkite/bintest/v3 v3.3.0
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.2.4
github.com/charmbracelet/huh v0.6.0
github.com/charmbracelet/lipgloss v1.0.0
github.com/coreos/go-oidc v2.2.1+incompatible // replaced
github.com/coreos/go-oidc/v3 v3.11.0
Expand Down Expand Up @@ -267,6 +268,7 @@ require (
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
github.com/apache/arrow/go/v15 v15.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
Expand All @@ -286,11 +288,13 @@ require (
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/charmbracelet/x/ansi v0.4.5 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudflare/cfssl v1.6.4 // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
Expand Down Expand Up @@ -434,6 +438,7 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.4.0 // indirect
Expand All @@ -444,7 +449,7 @@ require (
github.com/mtibben/percent v0.2.1 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
Expand Down
14 changes: 12 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.49.12/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
Expand Down Expand Up @@ -995,6 +997,8 @@ github.com/buildkite/interpolate v0.1.3/go.mod h1:UNVe6A+UfiBNKbhAySrBbZFZFxQ+DX
github.com/buildkite/roko v1.2.0 h1:hbNURz//dQqNl6Eo9awjQOVOZwSDJ8VEbBDxSfT9rGQ=
github.com/buildkite/roko v1.2.0/go.mod h1:23R9e6nHxgedznkwwfmqZ6+0VJZJZ2Sg/uVcp2cP46I=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
Expand All @@ -1015,10 +1019,14 @@ github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQW
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM=
github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8=
github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU=
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM=
github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
Expand Down Expand Up @@ -1834,6 +1842,8 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
Expand Down Expand Up @@ -1872,8 +1882,8 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg=
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
Expand Down
42 changes: 35 additions & 7 deletions lib/asciitable/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ package asciitable
import (
"bytes"
"fmt"
"io"
"os"
"slices"
"strings"
"text/tabwriter"

"github.com/gravitational/trace"
"golang.org/x/term"
)

Expand Down Expand Up @@ -158,10 +160,26 @@ func (t *Table) truncateCell(colIndex int, cell string) (string, bool) {
}

// AsBuffer returns a *bytes.Buffer with the printed output of the table.
//
// TODO(nklaassen): delete this, all calls either immediately copy the buffer to
// another writer or just call .String() once.
func (t *Table) AsBuffer() *bytes.Buffer {
var buffer bytes.Buffer
// Writes to bytes.Buffer never return an error.
_ = t.WriteTo(&buffer)
return &buffer
}

writer := tabwriter.NewWriter(&buffer, 5, 0, 1, ' ', 0)
func (t *Table) String() string {
var sb strings.Builder
// Writes to strings.Builder never return an error.
_ = t.WriteTo(&sb)
return sb.String()
}

// WriteTo writes the full table to [w] or else returns an error.
func (t *Table) WriteTo(w io.Writer) error {
writer := tabwriter.NewWriter(w, 5, 0, 1, ' ', 0)
template := strings.Repeat("%v\t", len(t.columns))

// Header and separator.
Expand All @@ -173,8 +191,12 @@ func (t *Table) AsBuffer() *bytes.Buffer {
colh = append(colh, col.Title)
cols = append(cols, strings.Repeat("-", col.width))
}
fmt.Fprintf(writer, template+"\n", colh...)
fmt.Fprintf(writer, template+"\n", cols...)
if _, err := fmt.Fprintf(writer, template+"\n", colh...); err != nil {
return trace.Wrap(err)
}
if _, err := fmt.Fprintf(writer, template+"\n", cols...); err != nil {
return trace.Wrap(err)
}
}

// Body.
Expand All @@ -188,17 +210,23 @@ func (t *Table) AsBuffer() *bytes.Buffer {
}
rowi = append(rowi, cell)
}
fmt.Fprintf(writer, template+"\n", rowi...)
if _, err := fmt.Fprintf(writer, template+"\n", rowi...); err != nil {
return trace.Wrap(err)
}
}

// Footnotes.
for label := range footnoteLabels {
fmt.Fprintln(writer)
fmt.Fprintln(writer, label, t.footnotes[label])
if _, err := fmt.Fprintln(writer); err != nil {
return trace.Wrap(err)
}
if _, err := fmt.Fprintln(writer, label, t.footnotes[label]); err != nil {
return trace.Wrap(err)
}
}

writer.Flush()
return &buffer
return nil
}

// IsHeadless returns true if none of the table title cells contains any text.
Expand Down
4 changes: 2 additions & 2 deletions lib/services/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func ParseShortcut(in string) (string, error) {
return types.KindKubeServer, nil
case types.KindLock, "locks":
return types.KindLock, nil
case types.KindDatabaseServer:
case types.KindDatabaseServer, "db_servers":
return types.KindDatabaseServer, nil
case types.KindNetworkRestrictions:
return types.KindNetworkRestrictions, nil
Expand All @@ -185,7 +185,7 @@ func ParseShortcut(in string) (string, error) {
return types.KindApp, nil
case types.KindAppServer, "app_servers":
return types.KindAppServer, nil
case types.KindWindowsDesktopService, "windows_service", "win_desktop_service", "win_service":
case types.KindWindowsDesktopService, "windows_service", "win_desktop_service", "win_service", "windows_desktop_services":
return types.KindWindowsDesktopService, nil
case types.KindWindowsDesktop, "win_desktop":
return types.KindWindowsDesktop, nil
Expand Down
43 changes: 5 additions & 38 deletions tool/tctl/common/auth_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,11 @@ type AuthCommand struct {
identityWriter identityfile.ConfigWriter
integration string

rotateGracePeriod time.Duration
rotateType string
rotateManualMode bool
rotateTargetPhase string
authRotate authRotateCommand

authGenerate *kingpin.CmdClause
authExport *kingpin.CmdClause
authSign *kingpin.CmdClause
authRotate *kingpin.CmdClause
authLS *kingpin.CmdClause
authCRL *kingpin.CmdClause
// testInsecureSkipVerify is used to skip TLS verification during tests
Expand Down Expand Up @@ -157,13 +153,7 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, config *servicecfg.Co
a.authSign.Flag("windows-sid", `Optional Security Identifier to embed in the certificate. Only used when --format is set to "windows"`).StringVar(&a.windowsSID)
a.authSign.Flag("omit-cdp", `Omit CRL Distribution Points from the cert. Only used when --format is set to "windows"`).BoolVar(&a.omitCDP)

a.authRotate = auth.Command("rotate", "Rotate certificate authorities in the cluster.")
a.authRotate.Flag("grace-period", "Grace period keeps previous certificate authorities signatures valid, if set to 0 will force users to re-login and nodes to re-register.").
Default(fmt.Sprintf("%v", defaults.RotationGracePeriod)).
DurationVar(&a.rotateGracePeriod)
a.authRotate.Flag("manual", "Activate manual rotation , set rotation phases manually").BoolVar(&a.rotateManualMode)
a.authRotate.Flag("type", fmt.Sprintf("Certificate authority to rotate, one of: %s", strings.Join(getCertAuthTypes(), ", "))).Required().EnumVar(&a.rotateType, getCertAuthTypes()...)
a.authRotate.Flag("phase", fmt.Sprintf("Target rotation phase to set, used in manual rotation, one of: %v", strings.Join(types.RotatePhases, ", "))).StringVar(&a.rotateTargetPhase)
a.authRotate.Initialize(auth)

a.authLS = auth.Command("ls", "List connected auth servers.")
a.authLS.Flag("format", "Output format: 'yaml', 'json' or 'text'").Default(teleport.YAML).StringVar(&a.format)
Expand All @@ -175,15 +165,16 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, config *servicecfg.Co
// TryRun takes the CLI command as an argument (like "auth gen") and executes it
// or returns match=false if 'cmd' does not belong to it
func (a *AuthCommand) TryRun(ctx context.Context, cmd string, client *authclient.Client) (match bool, err error) {
if match, err := a.authRotate.TryRun(ctx, cmd, client); match || err != nil {
return match, trace.Wrap(err)
}
switch cmd {
case a.authGenerate.FullCommand():
err = a.GenerateKeys(ctx, client)
case a.authExport.FullCommand():
err = a.ExportAuthorities(ctx, client)
case a.authSign.FullCommand():
err = a.GenerateAndSignKeys(ctx, client)
case a.authRotate.FullCommand():
err = a.RotateCertAuthority(ctx, client)
case a.authLS.FullCommand():
err = a.ListAuthServers(ctx, client)
case a.authCRL.FullCommand():
Expand Down Expand Up @@ -431,30 +422,6 @@ func (a *AuthCommand) generateSnowflakeKey(ctx context.Context, clusterAPI certi
writeHelperMessageDBmTLS(a.helperMsgDst(), filesWritten, "", a.outputFormat, "", a.streamTarfile))
}

// RotateCertAuthority starts or restarts certificate authority rotation process
func (a *AuthCommand) RotateCertAuthority(ctx context.Context, client *authclient.Client) error {
req := types.RotateRequest{
Type: types.CertAuthType(a.rotateType),
GracePeriod: &a.rotateGracePeriod,
TargetPhase: a.rotateTargetPhase,
}
if a.rotateManualMode {
req.Mode = types.RotationModeManual
} else {
req.Mode = types.RotationModeAuto
}
if err := client.RotateCertAuthority(ctx, req); err != nil {
return err
}
if a.rotateTargetPhase != "" {
fmt.Printf("Updated rotation phase to %q. To check status use 'tctl status'\n", a.rotateTargetPhase)
} else {
fmt.Printf("Initiated certificate authority rotation. To check status use 'tctl status'\n")
}

return nil
}

// ListAuthServers prints a list of connected auth servers
func (a *AuthCommand) ListAuthServers(ctx context.Context, clusterAPI *authclient.Client) error {
servers, err := clusterAPI.GetAuthServers()
Expand Down
Loading
Loading