go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/apps/cnquery/cmd/login.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package cmd 5 6 import ( 7 "context" 8 "os" 9 "strings" 10 "time" 11 12 "github.com/rs/zerolog/log" 13 "github.com/spf13/cobra" 14 "github.com/spf13/viper" 15 "go.mondoo.com/cnquery" 16 "go.mondoo.com/cnquery/cli/config" 17 "go.mondoo.com/cnquery/cli/sysinfo" 18 "go.mondoo.com/cnquery/providers-sdk/v1/upstream" 19 "go.mondoo.com/ranger-rpc" 20 "go.mondoo.com/ranger-rpc/plugins/authentication/statictoken" 21 ) 22 23 func init() { 24 rootCmd.AddCommand(LoginCmd) 25 LoginCmd.Flags().StringP("token", "t", "", "Set a client registration token.") 26 LoginCmd.Flags().StringToString("annotation", nil, "Set the client annotations.") 27 LoginCmd.Flags().String("name", "", "Set asset name.") 28 LoginCmd.Flags().String("api-endpoint", "", "Set the Mondoo API endpoint.") 29 } 30 31 var LoginCmd = &cobra.Command{ 32 Use: "login", 33 Aliases: []string{"register"}, 34 Short: "Register with Mondoo Platform.", 35 Long: ` 36 Log in to Mondoo Platform using a registration token. To pass in the token, use 37 the '--token' flag. 38 39 You can generate a new registration token on the Mondoo Dashboard. Go to 40 https://console.mondoo.com -> Space -> Settings -> Registration Token. Copy the token and pass it in 41 using the '--token' argument. 42 43 You remain logged in until you explicitly log out using the 'logout' subcommand. 44 `, 45 PreRun: func(cmd *cobra.Command, args []string) { 46 viper.BindPFlag("api_endpoint", cmd.Flags().Lookup("api-endpoint")) 47 viper.BindPFlag("name", cmd.Flags().Lookup("name")) 48 }, 49 Run: func(cmd *cobra.Command, args []string) { 50 token, _ := cmd.Flags().GetString("token") 51 annotations, _ := cmd.Flags().GetStringToString("annotation") 52 register(token, annotations) 53 }, 54 } 55 56 func register(token string, annotations map[string]string) { 57 var err error 58 var credential *upstream.ServiceAccountCredentials 59 60 // determine information about the client 61 sysInfo, err := sysinfo.GatherSystemInfo() 62 if err != nil { 63 log.Fatal().Err(err).Msg("could not gather client information") 64 } 65 defaultPlugins := defaultRangerPlugins(sysInfo, cnquery.DefaultFeatures) 66 67 apiEndpoint := viper.GetString("api_endpoint") 68 token = strings.TrimSpace(token) 69 70 // NOTE: login is special because we do not have a config yet 71 proxy, err := config.GetAPIProxy() 72 if err != nil { 73 log.Fatal().Err(err).Msg("could not parse proxy URL") 74 } 75 httpClient := ranger.NewHttpClient(ranger.WithProxy(proxy)) 76 77 // we handle three cases here: 78 // 1. user has a token provided 79 // 2. user has no token provided, but has a service account file is already there 80 // 81 if token != "" { 82 // print token details 83 claims, err := upstream.ExtractTokenClaims(token) 84 if err != nil { 85 log.Warn().Err(err).Msg("could not read the token") 86 } else { 87 if len(claims.Description) > 0 { 88 log.Info().Msg("token description: " + claims.Description) 89 } 90 if claims.IsExpired() { 91 log.Warn().Msg("token is expired") 92 } else { 93 log.Info().Msg("token will expire at " + claims.Claims.Expiry.Time().Format(time.RFC1123)) 94 } 95 96 if apiEndpoint == "" { 97 apiEndpoint = claims.ApiEndpoint 98 } 99 } 100 101 // gather service account 102 plugins := []ranger.ClientPlugin{} 103 plugins = append(plugins, defaultPlugins...) 104 plugins = append(plugins, statictoken.NewRangerPlugin(token)) 105 106 client, err := upstream.NewAgentManagerClient(apiEndpoint, httpClient, plugins...) 107 if err != nil { 108 log.Fatal().Err(err).Msg("could not connect to mondoo platform") 109 } 110 111 name := viper.GetString("name") 112 if name == "" { 113 name = sysInfo.Hostname 114 } 115 116 confirmation, err := client.RegisterAgent(context.Background(), &upstream.AgentRegistrationRequest{ 117 Token: token, 118 Name: name, 119 AgentInfo: &upstream.AgentInfo{ 120 Mrn: "", 121 Version: sysInfo.Version, 122 Build: sysInfo.Build, 123 PlatformName: sysInfo.Platform.Name, 124 PlatformRelease: sysInfo.Platform.Version, 125 PlatformArch: sysInfo.Platform.Arch, 126 PlatformIp: sysInfo.IP, 127 PlatformHostname: sysInfo.Hostname, 128 Labels: nil, 129 PlatformId: sysInfo.PlatformId, 130 }, 131 }) 132 if err != nil { 133 log.Fatal().Err(err).Msg("failed to log in client") 134 } 135 136 log.Debug().Msg("store configuration") 137 // overwrite force, otherwise it will be stored 138 viper.Set("force", false) 139 140 // update configuration file, api-endpoint is set automatically 141 viper.Set("agent_mrn", confirmation.AgentMrn) 142 viper.Set("api_endpoint", confirmation.Credential.ApiEndpoint) 143 viper.Set("space_mrn", confirmation.Credential.GetParentMrn()) 144 viper.Set("mrn", confirmation.Credential.Mrn) 145 viper.Set("private_key", confirmation.Credential.PrivateKey) 146 viper.Set("certificate", confirmation.Credential.Certificate) 147 viper.Set("annotations", annotations) 148 credential = confirmation.Credential 149 } else { 150 // try to read local options 151 opts, optsErr := config.Read() 152 if optsErr != nil { 153 log.Warn().Msg("could not load configuration, please use --token or --config with the appropriate values") 154 os.Exit(1) 155 } 156 // print the used config to the user 157 config.DisplayUsedConfig() 158 159 httpClient, err = opts.GetHttpClient() 160 if err != nil { 161 log.Warn().Err(err).Msg("could not create http client") 162 os.Exit(1) 163 } 164 165 if opts.AgentMrn != "" { 166 // already authenticated 167 log.Info().Msg("client is already logged in, skipping") 168 credential = opts.GetServiceCredential() 169 } else { 170 credential = opts.GetServiceCredential() 171 172 // run ping pong 173 plugins := []ranger.ClientPlugin{} 174 plugins = append(plugins, defaultPlugins...) 175 certAuth, err := upstream.NewServiceAccountRangerPlugin(credential) 176 if err != nil { 177 log.Warn().Err(err).Msg("could not initialize certificate authentication") 178 os.Exit(1) 179 } 180 plugins = append(plugins, certAuth) 181 182 client, err := upstream.NewAgentManagerClient(apiEndpoint, httpClient, plugins...) 183 if err != nil { 184 log.Warn().Err(err).Msg("could not connect to Mondoo Platform") 185 os.Exit(1) 186 } 187 188 name := viper.GetString("name") 189 if name == "" { 190 name = sysInfo.Hostname 191 } 192 193 confirmation, err := client.RegisterAgent(context.Background(), &upstream.AgentRegistrationRequest{ 194 Name: name, 195 AgentInfo: &upstream.AgentInfo{ 196 Mrn: opts.AgentMrn, 197 Version: sysInfo.Version, 198 Build: sysInfo.Build, 199 PlatformName: sysInfo.Platform.Name, 200 PlatformRelease: sysInfo.Platform.Version, 201 PlatformArch: sysInfo.Platform.Arch, 202 PlatformIp: sysInfo.IP, 203 PlatformHostname: sysInfo.Hostname, 204 Labels: opts.Labels, 205 PlatformId: sysInfo.PlatformId, 206 }, 207 }) 208 if err != nil { 209 log.Fatal().Err(err).Msg("failed to log in client") 210 } 211 212 // update configuration file, api-endpoint is set automatically 213 // NOTE: we ignore the credentials from confirmation since the service never returns the credentials again 214 viper.Set("agent_mrn", confirmation.AgentMrn) 215 } 216 } 217 218 err = config.StoreConfig() 219 if err != nil { 220 log.Warn().Err(err).Msg("could not write mondoo configuration") 221 os.Exit(1) 222 } 223 224 // run ping pong to validate the service account 225 plugins := []ranger.ClientPlugin{} 226 plugins = append(plugins, defaultPlugins...) 227 certAuth, err := upstream.NewServiceAccountRangerPlugin(credential) 228 if err != nil { 229 log.Warn().Err(err).Msg("could not initialize certificate authentication") 230 } 231 plugins = append(plugins, certAuth) 232 client, err := upstream.NewAgentManagerClient(apiEndpoint, httpClient, plugins...) 233 if err != nil { 234 log.Warn().Err(err).Msg("could not connect to mondoo platform") 235 os.Exit(1) 236 } 237 238 _, err = client.PingPong(context.Background(), &upstream.Ping{}) 239 if err != nil { 240 log.Warn().Msg(err.Error()) 241 os.Exit(1) 242 } 243 244 log.Info().Msgf("client %s has logged in successfully", viper.Get("agent_mrn")) 245 }