go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/apps/cnquery/cmd/status.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package cmd 5 6 import ( 7 "context" 8 "encoding/json" 9 "os" 10 "strings" 11 "time" 12 13 "github.com/rs/zerolog/log" 14 "github.com/spf13/cobra" 15 "github.com/spf13/viper" 16 "go.mondoo.com/cnquery" 17 "go.mondoo.com/cnquery/cli/config" 18 "go.mondoo.com/cnquery/cli/sysinfo" 19 "go.mondoo.com/cnquery/cli/theme" 20 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 21 "go.mondoo.com/cnquery/providers-sdk/v1/upstream" 22 "go.mondoo.com/cnquery/providers-sdk/v1/upstream/health" 23 "go.mondoo.com/ranger-rpc" 24 "sigs.k8s.io/yaml" 25 ) 26 27 func init() { 28 StatusCmd.Flags().StringP("output", "o", "", "Set output format. Accepts json or yaml.") 29 rootCmd.AddCommand(StatusCmd) 30 } 31 32 // StatusCmd represents the version command 33 var StatusCmd = &cobra.Command{ 34 Use: "status", 35 Short: "Verify access to Mondoo Platform.", 36 Long: ` 37 Status sends a ping to Mondoo Platform to verify the credentials. 38 `, 39 PreRun: func(cmd *cobra.Command, args []string) { 40 viper.BindPFlag("output", cmd.Flags().Lookup("output")) 41 }, 42 Run: func(cmd *cobra.Command, args []string) { 43 opts, optsErr := config.Read() 44 if optsErr != nil { 45 log.Fatal().Err(optsErr).Msg("could not load configuration") 46 } 47 48 config.DisplayUsedConfig() 49 50 s := Status{ 51 Client: ClientStatus{ 52 Timestamp: time.Now().Format(time.RFC3339), 53 Version: cnquery.GetVersion(), 54 Build: cnquery.GetBuild(), 55 }, 56 } 57 58 httpClient, err := opts.GetHttpClient() 59 if err != nil { 60 log.Fatal().Err(err).Msg("failed to set up Mondoo API client") 61 } 62 63 sysInfo, err := sysinfo.GatherSystemInfo() 64 if err == nil { 65 s.Client.Platform = sysInfo.Platform 66 s.Client.Hostname = sysInfo.Hostname 67 s.Client.IP = sysInfo.IP 68 } 69 70 // check server health and clock skew 71 upstreamStatus, err := health.CheckApiHealth(httpClient, opts.UpstreamApiEndpoint()) 72 if err != nil { 73 log.Error().Err(err).Msg("could not check upstream health") 74 } 75 s.Upstream = upstreamStatus 76 77 // check valid agent authentication 78 plugins := []ranger.ClientPlugin{} 79 80 // try to load config into credentials struct 81 credentials := opts.GetServiceCredential() 82 if credentials != nil && len(credentials.Mrn) > 0 { 83 s.Client.ParentMrn = credentials.ParentMrn 84 s.Client.Registered = true 85 s.Client.ServiceAccount = credentials.Mrn 86 s.Client.Mrn = opts.AgentMrn 87 if s.Client.Mrn == "" { 88 s.Client.Mrn = "no managed client" 89 } 90 91 certAuth, err := upstream.NewServiceAccountRangerPlugin(credentials) 92 if err != nil { 93 log.Fatal().Err(err).Msg("invalid credentials") 94 } 95 plugins = append(plugins, certAuth) 96 97 // try to ping the server 98 client, err := upstream.NewAgentManagerClient(s.Upstream.API.Endpoint, httpClient, plugins...) 99 if err == nil { 100 _, err = client.PingPong(context.Background(), &upstream.Ping{}) 101 if err != nil { 102 s.Client.PingPongError = err 103 } 104 } else { 105 s.Client.PingPongError = err 106 } 107 } 108 109 switch strings.ToLower(viper.GetString("output")) { 110 case "yaml": 111 s.RenderYaml() 112 case "json": 113 s.RenderJson() 114 default: 115 s.RenderCliStatus() 116 } 117 118 if !s.Client.Registered || s.Client.PingPongError != nil { 119 os.Exit(1) 120 } 121 }, 122 } 123 124 type Status struct { 125 Client ClientStatus `json:"client"` 126 Upstream health.Status `json:"upstream"` 127 } 128 129 type ClientStatus struct { 130 Timestamp string `json:"timestamp,omitempty"` 131 Mrn string `json:"mrn,omitempty"` 132 ServiceAccount string `json:"service_account,omitempty"` 133 ParentMrn string `json:"parentMrn,omitempty"` 134 Version string `json:"version,omitempty"` 135 Build string `json:"build,omitempty"` 136 Labels map[string]string `json:"labels,omitempty"` 137 Platform *inventory.Platform `json:"platform,omitempty"` 138 IP string `json:"ip,omitempty"` 139 Hostname string `json:"hostname,omitempty"` 140 Registered bool `json:"registered,omitempty"` 141 PingPongError error `json:"pingPongError,omitempty"` 142 } 143 144 func (s Status) RenderCliStatus() { 145 if s.Client.Platform != nil { 146 agent := s.Client 147 log.Info().Msg("Platform:\t" + agent.Platform.Name) 148 log.Info().Msg("Version:\t" + agent.Platform.Version) 149 log.Info().Msg("Hostname:\t" + agent.Hostname) 150 log.Info().Msg("IP:\t\t" + agent.IP) 151 } else { 152 log.Warn().Msg("could not determine client platform information") 153 } 154 155 log.Info().Msg("Time:\t\t" + s.Client.Timestamp) 156 log.Info().Msg("Version:\t" + cnquery.GetVersion() + " (API Version: " + cnquery.APIVersion() + ")") 157 158 log.Info().Msg("API ConnectionConfig:\t" + s.Upstream.API.Endpoint) 159 log.Info().Msg("API Status:\t" + s.Upstream.API.Status) 160 log.Info().Msg("API Time:\t" + s.Upstream.API.Timestamp) 161 log.Info().Msg("API Version:\t" + s.Upstream.API.Version) 162 163 if s.Upstream.API.Version != cnquery.APIVersion() { 164 log.Warn().Msg("API versions do not match, please update the client") 165 } 166 167 if len(s.Upstream.Features) > 0 { 168 log.Info().Msg("Features:\t" + strings.Join(s.Upstream.Features, ",")) 169 } 170 171 if s.Client.ParentMrn == "" { 172 log.Info().Msg("Owner:\t" + s.Client.ParentMrn) 173 } 174 175 if s.Client.Registered { 176 log.Info().Msg("Client:\t" + s.Client.Mrn) 177 log.Info().Msg("Service Account:\t" + s.Client.ServiceAccount) 178 log.Info().Msg(theme.DefaultTheme.Success("client is registered")) 179 } else { 180 log.Error().Msg("client is not registered") 181 } 182 183 if s.Client.Registered && s.Client.PingPongError == nil { 184 log.Info().Msg(theme.DefaultTheme.Success("client authenticated successfully")) 185 } else if s.Client.PingPongError == nil { 186 log.Error().Err(s.Client.PingPongError). 187 Msgf("The Mondoo Platform credentials provided at %s didn't successfully authenticate with Mondoo Platform. Please re-authenticate with Mondoo Platform. To learn how, read https://mondoo.com/docs/cnspec/cnspec-adv-install/registration.", 188 viper.ConfigFileUsed()) 189 } 190 191 for i := range s.Upstream.Warnings { 192 log.Warn().Msg(s.Upstream.Warnings[i]) 193 } 194 } 195 196 func (s Status) RenderJson() { 197 output, err := json.Marshal(s) 198 if err != nil { 199 log.Error().Err(err).Msg("could not generate json") 200 } 201 os.Stdout.Write(output) 202 } 203 204 func (s Status) RenderYaml() { 205 output, err := yaml.Marshal(s) 206 if err != nil { 207 log.Error().Err(err).Msg("could not generate yaml") 208 } 209 os.Stdout.Write(output) 210 }