From 9d9f61cd516ff50644095a234f2d8c05c9eff890 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Wed, 15 Jan 2025 18:35:33 -0300 Subject: [PATCH 1/5] Support multiple active CAs in `tctl auth export` --- tool/tctl/common/auth_command.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index 8800f3aca7f72..fe7fa67ba3eee 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -75,6 +75,7 @@ type AuthCommand struct { genTTL time.Duration exportAuthorityFingerprint string exportPrivateKeys bool + exportOutputPrefix string output string outputFormat identityfile.Format compatVersion string @@ -123,6 +124,8 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIF fmt.Sprintf("export certificate type (%v)", strings.Join(allowedCertificateTypes, ", "))). EnumVar(&a.authType, allowedCertificateTypes...) a.authExport.Flag("integration", "Name of the integration. Only applies to \"github\" CAs.").StringVar(&a.integration) + a.authExport.Flag("out-prefix", "If set writes exported authorities to files with the given prefix"). + StringVar(&a.exportOutputPrefix) 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) @@ -233,9 +236,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 authCommandClient) error { - exportFunc := client.ExportAuthorities + exportFunc := client.ExportAllAuthorities if a.exportPrivateKeys { - exportFunc = client.ExportAuthoritiesSecrets + exportFunc = client.ExportAllAuthoritiesSecrets } authorities, err := exportFunc( @@ -252,8 +255,28 @@ func (a *AuthCommand) ExportAuthorities(ctx context.Context, clt authCommandClie return trace.Wrap(err) } - fmt.Println(authorities) + if l := len(authorities); l > 1 && a.exportOutputPrefix == "" { + return trace.BadParameter("found %d authorities to export, use --out-prefix to export all", l) + } + + if a.exportOutputPrefix != "" { + perms := os.FileMode(0644) + if a.exportPrivateKeys { + perms = 0600 + } + + for i, authority := range authorities { + name := fmt.Sprintf("%s%d.cer", a.exportOutputPrefix, i) + if err := os.WriteFile(name, authority.Data, perms); err != nil { + return trace.Wrap(err) + } + fmt.Printf("Wrote %s\n", name) + } + return nil + } + // Only a single CA is exported if we got this far. + fmt.Printf("%s\n", authorities[0].Data) return nil } From 4dd253cb3e53a143f8b1fe1c9a068a6825740cb1 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Tue, 21 Jan 2025 14:27:21 -0300 Subject: [PATCH 2/5] Remove the ExportAuthoritiesSecrets func --- lib/client/ca_export.go | 9 --------- lib/client/ca_export_test.go | 3 --- 2 files changed, 12 deletions(-) diff --git a/lib/client/ca_export.go b/lib/client/ca_export.go index 4fbd59fc4df98..5331f0c2a1923 100644 --- a/lib/client/ca_export.go +++ b/lib/client/ca_export.go @@ -165,15 +165,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 5e7004eb88543..bfd1d5f428190 100644 --- a/lib/client/ca_export_test.go +++ b/lib/client/ca_export_test.go @@ -353,9 +353,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) - }) }) } } From 3e7b879e8afb2e6630da1a3cf6f3ccea0db78ef3 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Tue, 21 Jan 2025 18:26:57 -0300 Subject: [PATCH 3/5] Mention path in the --out-prefix help --- tool/tctl/common/auth_command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index fe7fa67ba3eee..738b8ef0601a5 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -124,7 +124,7 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIF fmt.Sprintf("export certificate type (%v)", strings.Join(allowedCertificateTypes, ", "))). EnumVar(&a.authType, allowedCertificateTypes...) a.authExport.Flag("integration", "Name of the integration. Only applies to \"github\" CAs.").StringVar(&a.integration) - a.authExport.Flag("out-prefix", "If set writes exported authorities to files with the given prefix"). + a.authExport.Flag("out-prefix", "If set writes exported authorities to files with the given path prefix"). StringVar(&a.exportOutputPrefix) a.authGenerate = auth.Command("gen", "Generate a new SSH keypair.").Hidden() From 7458e1e2b960ab55563bc052db4573cc3c972a11 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Tue, 21 Jan 2025 18:30:45 -0300 Subject: [PATCH 4/5] Rename --out-prefix to --out --- tool/tctl/common/auth_command.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index 738b8ef0601a5..9510b3e938e2f 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -75,7 +75,6 @@ type AuthCommand struct { genTTL time.Duration exportAuthorityFingerprint string exportPrivateKeys bool - exportOutputPrefix string output string outputFormat identityfile.Format compatVersion string @@ -124,8 +123,9 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIF fmt.Sprintf("export certificate type (%v)", strings.Join(allowedCertificateTypes, ", "))). EnumVar(&a.authType, allowedCertificateTypes...) a.authExport.Flag("integration", "Name of the integration. Only applies to \"github\" CAs.").StringVar(&a.integration) - a.authExport.Flag("out-prefix", "If set writes exported authorities to files with the given path prefix"). - StringVar(&a.exportOutputPrefix) + 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) @@ -255,18 +255,18 @@ func (a *AuthCommand) ExportAuthorities(ctx context.Context, clt authCommandClie return trace.Wrap(err) } - if l := len(authorities); l > 1 && a.exportOutputPrefix == "" { - return trace.BadParameter("found %d authorities to export, use --out-prefix to export all", l) + if l := len(authorities); l > 1 && a.output == "" { + return trace.BadParameter("found %d authorities to export, use --out to export all", l) } - if a.exportOutputPrefix != "" { + if a.output != "" { perms := os.FileMode(0644) if a.exportPrivateKeys { perms = 0600 } for i, authority := range authorities { - name := fmt.Sprintf("%s%d.cer", a.exportOutputPrefix, i) + name := fmt.Sprintf("%s%d.cer", a.output, i) if err := os.WriteFile(name, authority.Data, perms); err != nil { return trace.Wrap(err) } From 743f4f22bd3fc94dcbd9884f3b3c8b91693c0aac Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Tue, 21 Jan 2025 18:32:28 -0300 Subject: [PATCH 5/5] Write script-friendly output --- tool/tctl/common/auth_command.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go index 9510b3e938e2f..b11b9643c69be 100644 --- a/tool/tctl/common/auth_command.go +++ b/tool/tctl/common/auth_command.go @@ -265,12 +265,13 @@ func (a *AuthCommand) ExportAuthorities(ctx context.Context, clt authCommandClie 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.Printf("Wrote %s\n", name) + fmt.Println(name) } return nil }