Skip to content

Commit

Permalink
feat: enable version API in maintenance mode
Browse files Browse the repository at this point in the history
Version API is only available over SideroLink connection.

This is useful to find Talos version as it got booted (e.g. to generate
proper machine configuration).

There's a security concern that version API might return sensitive
information via public API. At the same time Talos version can be
guessed by looking at the output of other APIs, e.g. resource type list
(`talosctl get rd`), which changes with every minor version.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
  • Loading branch information
smira committed May 26, 2022
1 parent 875f67a commit fe85804
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 36 deletions.
76 changes: 43 additions & 33 deletions cmd/talosctl/cmd/talos/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var versionCmdFlags struct {
clientOnly bool
shortVersion bool
json bool
insecure bool
}

// versionCmd represents the `talosctl version` command.
Expand All @@ -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)
}
return WithClient(cmdVersion)
},
}

defaultNode := client.AddrFromPeer(&remotePeer)
func cmdVersion(ctx context.Context, c *client.Client) error {
var remotePeer peer.Peer

for _, msg := range resp.Messages {
node := defaultNode
resp, err := c.Version(ctx, grpc.Peer(&remotePeer))
if err != nil {
if resp == nil {
return fmt.Errorf("error getting version: %s", err)
}

if msg.Metadata != nil {
node = msg.Metadata.Hostname
}
cli.Warning("%s", err)
}

if !versionCmdFlags.json {
fmt.Printf("\t%s: %s\n", "NODE", node)
defaultNode := client.AddrFromPeer(&remotePeer)

version.PrintLongVersionFromExisting(msg.Version)
for _, msg := range resp.Messages {
node := defaultNode

var enabledFeatures []string
if msg.Features.GetRbac() {
enabledFeatures = append(enabledFeatures, "RBAC")
}
if msg.Metadata != nil {
node = msg.Metadata.Hostname
}

fmt.Printf("\tEnabled: %s\n", strings.Join(enabledFeatures, ", "))
if !versionCmdFlags.json {
fmt.Printf("\t%s: %s\n", "NODE", node)

continue
}
version.PrintLongVersionFromExisting(msg.Version)

b, err := protojson.Marshal(msg)
if err != nil {
return err
}
fmt.Printf("%s\n", b)
var enabledFeatures []string
if msg.Features.GetRbac() {
enabledFeatures = append(enabledFeatures, "RBAC")
}

return nil
})
},
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, "")
Expand Down
56 changes: 56 additions & 0 deletions internal/app/maintenance/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
7 changes: 4 additions & 3 deletions website/content/v1.1/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit fe85804

Please sign in to comment.