From 604209626e5735835125cbb5fd4ded2a7cff2f94 Mon Sep 17 00:00:00 2001 From: Noah Stride Date: Tue, 26 Mar 2024 09:21:48 +0000 Subject: [PATCH] [v15] Add `spiffe-inspect` test command to tbot (#39759) * Add `spiffe-inspect` test command to tbot * Use fmt.Printf instead of log.Infof * Review comments --- tool/tbot/main.go | 6 ++++ tool/tbot/spiffe.go | 76 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tool/tbot/spiffe.go diff --git a/tool/tbot/main.go b/tool/tbot/main.go index 0a581c92c48fc..c294055f8078f 100644 --- a/tool/tbot/main.go +++ b/tool/tbot/main.go @@ -162,6 +162,10 @@ func Run(args []string, stdout io.Writer) error { kubeCredentialsCmd := kubeCmd.Command("credentials", "Get credentials for kubectl access").Hidden() kubeCredentialsCmd.Flag("destination-dir", "The destination directory with which to generate Kubernetes credentials").Required().StringVar(&cf.DestinationDir) + spiffeInspectPath := "" + spiffeInspectCmd := app.Command("spiffe-inspect", "Inspects a SPIFFE Workload API endpoint to ensure it is working correctly.") + spiffeInspectCmd.Flag("path", "The path to the SPIFFE Workload API endpoint to test.").Required().StringVar(&spiffeInspectPath) + utils.UpdateAppUsageTemplate(app, args) command, err := app.Parse(args) if err != nil { @@ -233,6 +237,8 @@ func Run(args []string, stdout io.Writer) error { err = onProxyCommand(botConfig, &cf) case kubeCredentialsCmd.FullCommand(): err = onKubeCredentialsCommand(botConfig) + case spiffeInspectCmd.FullCommand(): + err = onSPIFFEInspect(spiffeInspectPath) default: // This should only happen when there's a missing switch case above. err = trace.BadParameter("command %q not configured", command) diff --git a/tool/tbot/spiffe.go b/tool/tbot/spiffe.go new file mode 100644 index 0000000000000..d1b0b2a85dbc7 --- /dev/null +++ b/tool/tbot/spiffe.go @@ -0,0 +1,76 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package main + +import ( + "context" + "fmt" + + "github.com/gravitational/trace" + "github.com/spiffe/go-spiffe/v2/workloadapi" +) + +func onSPIFFEInspect(path string) error { + ctx := context.Background() + log.WithField("path", path).Info("Inspecting SPIFFE Workload API Endpoint") + + source, err := workloadapi.New(ctx, workloadapi.WithLogger(log), workloadapi.WithAddr(path)) + if err != nil { + return trace.Wrap(err, "creating x509 source") + } + defer source.Close() + + res, err := source.FetchX509Context(ctx) + if err != nil { + return trace.Wrap(err, "getting x509 context") + } + + log. + WithField("svids_count", len(res.SVIDs)). + WithField("bundles_count", res.Bundles.Len()). + Info("Received X.509 SVID context from Workload API") + + if len(res.SVIDs) == 0 { + log.Error("No SVIDs received, check your configuration.") + } else { + fmt.Println("SVIDS") + for _, svid := range res.SVIDs { + fmt.Printf("- %s\n", svid.ID.String()) + fmt.Printf(" - Hint: %s\n", svid.Hint) + fmt.Printf(" - Expiry: %s\n", svid.Certificates[0].NotAfter) + for _, san := range svid.Certificates[0].DNSNames { + fmt.Printf(" - DNS SAN: %s\n", san) + } + for _, san := range svid.Certificates[0].IPAddresses { + fmt.Printf(" - IP SAN: %s\n", san) + } + } + } + + if res.Bundles.Len() == 0 { + log.Error("No trust bundles received, check your configuration.") + } else { + fmt.Println("Trust Bundles") + for _, bundle := range res.Bundles.Bundles() { + fmt.Printf("- %s\n", bundle.TrustDomain()) + } + } + + return nil +}