diff --git a/cmd/talosctl/cmd/talos/version.go b/cmd/talosctl/cmd/talos/version.go index 08ba81c3d..7a05557b9 100644 --- a/cmd/talosctl/cmd/talos/version.go +++ b/cmd/talosctl/cmd/talos/version.go @@ -24,6 +24,7 @@ var versionCmdFlags struct { clientOnly bool shortVersion bool json bool + insecure bool } // versionCmd represents the `talosctl version` command. @@ -49,56 +50,65 @@ var versionCmd = &cobra.Command{ fmt.Println("Server:") } - return WithClient(func(ctx context.Context, c *client.Client) error { - var remotePeer peer.Peer + if versionCmdFlags.insecure { + return WithClientMaintenance(nil, cmdVersion) + } - resp, err := c.Version(ctx, grpc.Peer(&remotePeer)) - if err != nil { - if resp == nil { - return fmt.Errorf("error getting version: %s", err) - } - cli.Warning("%s", err) - } - - defaultNode := client.AddrFromPeer(&remotePeer) - - for _, msg := range resp.Messages { - node := defaultNode - - if msg.Metadata != nil { - node = msg.Metadata.Hostname - } - - if !versionCmdFlags.json { - fmt.Printf("\t%s: %s\n", "NODE", node) - - version.PrintLongVersionFromExisting(msg.Version) - - var enabledFeatures []string - if msg.Features.GetRbac() { - enabledFeatures = append(enabledFeatures, "RBAC") - } - - fmt.Printf("\tEnabled: %s\n", strings.Join(enabledFeatures, ", ")) - - continue - } - - b, err := protojson.Marshal(msg) - if err != nil { - return err - } - fmt.Printf("%s\n", b) - } - - return nil - }) + return WithClient(cmdVersion) }, } +func cmdVersion(ctx context.Context, c *client.Client) error { + var remotePeer peer.Peer + + resp, err := c.Version(ctx, grpc.Peer(&remotePeer)) + if err != nil { + if resp == nil { + return fmt.Errorf("error getting version: %s", err) + } + + cli.Warning("%s", err) + } + + defaultNode := client.AddrFromPeer(&remotePeer) + + for _, msg := range resp.Messages { + node := defaultNode + + if msg.Metadata != nil { + node = msg.Metadata.Hostname + } + + if !versionCmdFlags.json { + fmt.Printf("\t%s: %s\n", "NODE", node) + + version.PrintLongVersionFromExisting(msg.Version) + + var enabledFeatures []string + if msg.Features.GetRbac() { + enabledFeatures = append(enabledFeatures, "RBAC") + } + + fmt.Printf("\tEnabled: %s\n", strings.Join(enabledFeatures, ", ")) + + continue + } + + b, err := protojson.Marshal(msg) + if err != nil { + return err + } + + fmt.Printf("%s\n", b) + } + + return nil +} + func init() { versionCmd.Flags().BoolVar(&versionCmdFlags.shortVersion, "short", false, "Print the short version") versionCmd.Flags().BoolVar(&versionCmdFlags.clientOnly, "client", false, "Print client version only") + versionCmd.Flags().BoolVarP(&versionCmdFlags.insecure, "insecure", "i", false, "use Talos maintenance mode API") // TODO remove when https://github.com/talos-systems/talos/issues/907 is implemented versionCmd.Flags().BoolVar(&versionCmdFlags.json, "json", false, "") diff --git a/internal/app/maintenance/server/server.go b/internal/app/maintenance/server/server.go index 6b3dc35fa..736521fba 100644 --- a/internal/app/maintenance/server/server.go +++ b/internal/app/maintenance/server/server.go @@ -8,11 +8,15 @@ import ( "context" "fmt" "log" + "net" "strings" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/peer" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" + "inet.af/netaddr" "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "github.com/talos-systems/talos/internal/app/resources" @@ -23,6 +27,8 @@ import ( "github.com/talos-systems/talos/pkg/machinery/api/storage" "github.com/talos-systems/talos/pkg/machinery/config/configloader" v1alpha1machine "github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine" + "github.com/talos-systems/talos/pkg/machinery/resources/network" + "github.com/talos-systems/talos/pkg/version" ) // Server implements machine.MachineService, network.NetworkService, and storage.StorageService. @@ -116,3 +122,53 @@ func (s *Server) GenerateConfiguration(ctx context.Context, in *machine.Generate func (s *Server) GenerateClientConfiguration(ctx context.Context, in *machine.GenerateClientConfigurationRequest) (*machine.GenerateClientConfigurationResponse, error) { return nil, status.Error(codes.Unimplemented, "client configuration (talosconfig) can't be generated in the maintenance mode") } + +func verifyPeer(ctx context.Context, condition func(netaddr.IP) bool) bool { + remotePeer, ok := peer.FromContext(ctx) + if !ok { + return false + } + + if remotePeer.Addr.Network() != "tcp" { + return false + } + + ip, _, err := net.SplitHostPort(remotePeer.Addr.String()) + if err != nil { + return false + } + + addr, err := netaddr.ParseIP(ip) + if err != nil { + return false + } + + return condition(addr) +} + +// Version implements the machine.MachineServer interface. +func (s *Server) Version(ctx context.Context, in *emptypb.Empty) (*machine.VersionResponse, error) { + if !verifyPeer(ctx, func(addr netaddr.IP) bool { + return network.IsULA(addr, network.ULASideroLink) + }) { + return nil, status.Error(codes.Unimplemented, "Version API is not implemented in maintenance mode") + } + + var platform *machine.PlatformInfo + + if s.runtime.State().Platform() != nil { + platform = &machine.PlatformInfo{ + Name: s.runtime.State().Platform().Name(), + Mode: s.runtime.State().Platform().Mode().String(), + } + } + + return &machine.VersionResponse{ + Messages: []*machine.Version{ + { + Version: version.NewVersion(), + Platform: platform, + }, + }, + }, nil +} diff --git a/website/content/v1.1/reference/cli.md b/website/content/v1.1/reference/cli.md index e555a43d0..d2274c87d 100644 --- a/website/content/v1.1/reference/cli.md +++ b/website/content/v1.1/reference/cli.md @@ -2094,9 +2094,10 @@ talosctl version [flags] ### Options ``` - --client Print client version only - -h, --help help for version - --short Print the short version + --client Print client version only + -h, --help help for version + -i, --insecure use Talos maintenance mode API + --short Print the short version ``` ### Options inherited from parent commands