From f2c9ea4bb080971016b4d290f5116d4850414cc8 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Wed, 22 Jan 2025 17:22:21 -0300 Subject: [PATCH] feat: Support multiple active CAs in `tctl auth export` (#51298) (#51376) * Support multiple active CAs in `tctl auth export` * Remove the ExportAuthoritiesSecrets func * Mention path in the --out-prefix help * Rename --out-prefix to --out * Write script-friendly output --- lib/client/ca_export.go | 9 --------- lib/client/ca_export_test.go | 3 --- tool/tctl/common/auth_command.go | 30 +++++++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/lib/client/ca_export.go b/lib/client/ca_export.go index 98aef2a02b1a5..4e7ec95d98671 100644 --- a/lib/client/ca_export.go +++ b/lib/client/ca_export.go @@ -132,15 +132,6 @@ func ExportAuthorities(ctx context.Context, client authclient.ClientI, req Expor return exportAuthorities(ctx, client, req, ExportAllAuthorities) } -// ExportAuthoritiesSecrets is the single-authority variant of -// [ExportAllAuthoritiesSecrets]. -// Soft-deprecated, prefer using [ExportAllAuthoritiesSecrets] and handling -// exports with more than one authority gracefully. -func ExportAuthoritiesSecrets(ctx context.Context, client authclient.ClientI, req ExportAuthoritiesRequest) (string, error) { - // TODO(codingllama): Remove ExportAuthoritiesSecrets. - return exportAuthorities(ctx, client, req, ExportAllAuthoritiesSecrets) -} - func exportAuthorities( ctx context.Context, client authclient.ClientI, diff --git a/lib/client/ca_export_test.go b/lib/client/ca_export_test.go index b2529c870b452..ccb0b5cdf5d3c 100644 --- a/lib/client/ca_export_test.go +++ b/lib/client/ca_export_test.go @@ -304,9 +304,6 @@ func TestExportAuthorities(t *testing.T) { t.Run(fmt.Sprintf("%s/ExportAllAuthoritiesSecrets", tt.name), func(t *testing.T) { runTest(t, ExportAllAuthoritiesSecrets, tt.assertSecrets) }) - t.Run(fmt.Sprintf("%s/ExportAuthoritiesSecrets", tt.name), func(t *testing.T) { - runUnaryTest(t, ExportAuthoritiesSecrets, tt.assertSecrets) - }) }) } } diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index e32f2a56dc2fb..33580adcf6ba0 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -115,6 +115,9 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, config *servicecfg.Co a.authExport.Flag("type", fmt.Sprintf("export certificate type (%v)", strings.Join(allowedCertificateTypes, ", "))). EnumVar(&a.authType, allowedCertificateTypes...) + a.authExport. + Flag("out", "If set writes exported authorities to files with the given path prefix"). + StringVar(&a.output) a.authGenerate = auth.Command("gen", "Generate a new SSH keypair.").Hidden() a.authGenerate.Flag("pub-key", "path to the public key").Required().StringVar(&a.genPubPath) @@ -220,9 +223,9 @@ var allowedCRLCertificateTypes = []string{ // If --type flag is given, only prints keys for CAs of this type, otherwise // prints all keys func (a *AuthCommand) ExportAuthorities(ctx context.Context, clt *authclient.Client) error { - exportFunc := client.ExportAuthorities + exportFunc := client.ExportAllAuthorities if a.exportPrivateKeys { - exportFunc = client.ExportAuthoritiesSecrets + exportFunc = client.ExportAllAuthoritiesSecrets } authorities, err := exportFunc( @@ -238,8 +241,29 @@ func (a *AuthCommand) ExportAuthorities(ctx context.Context, clt *authclient.Cli return trace.Wrap(err) } - fmt.Println(authorities) + if l := len(authorities); l > 1 && a.output == "" { + return trace.BadParameter("found %d authorities to export, use --out to export all", l) + } + + if a.output != "" { + perms := os.FileMode(0644) + if a.exportPrivateKeys { + perms = 0600 + } + + fmt.Fprintf(os.Stderr, "Writing %d files with prefix %q\n", len(authorities), a.output) + for i, authority := range authorities { + name := fmt.Sprintf("%s%d.cer", a.output, i) + if err := os.WriteFile(name, authority.Data, perms); err != nil { + return trace.Wrap(err) + } + fmt.Println(name) + } + return nil + } + // Only a single CA is exported if we got this far. + fmt.Printf("%s\n", authorities[0].Data) return nil }