diff --git a/cmd/minikube/cmd/audit.go b/cmd/minikube/cmd/audit.go new file mode 100644 index 000000000000..c3a855e9ff93 --- /dev/null +++ b/cmd/minikube/cmd/audit.go @@ -0,0 +1,55 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "os" + "os/user" + "strings" + "time" + + "github.com/spf13/viper" + "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/out/register" +) + +// getUser pulls the user flag, if empty gets the os user +func getUser() string { + u := viper.GetString(userFlag) + if u != "" { + return u + } + osUser, err := user.Current() + if err != nil { + return "unable to get user" + } + return osUser.Username +} + +// getArgs concats the args into space delimited string +func getArgs() string { + if len(os.Args) < 3 { + return "" + } + return strings.Join(os.Args[2:], " ") +} + +// audit logs details about the executed command +func audit(startTime time.Time) { + register.SetEventLogPath(localpath.EventLog("audit")) + register.RecordAudit(os.Args[1], getArgs(), getUser(), startTime, time.Now()) +} diff --git a/cmd/minikube/cmd/delete.go b/cmd/minikube/cmd/delete.go index 7bfe9bfda21e..ef19d80eb703 100644 --- a/cmd/minikube/cmd/delete.go +++ b/cmd/minikube/cmd/delete.go @@ -23,6 +23,7 @@ import ( "os/exec" "path/filepath" "strconv" + "time" "github.com/docker/machine/libmachine/mcnerror" "github.com/mitchellh/go-ps" @@ -129,6 +130,9 @@ func runDelete(cmd *cobra.Command, args []string) { if len(args) > 0 { exit.Message(reason.Usage, "Usage: minikube delete") } + + defer audit(time.Now()) + // register.SetEventLogPath(localpath.EventLog(ClusterFlagValue())) register.Reg.SetStep(register.Deleting) diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 1c1d7b6c6a52..de3f4a4817e7 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -28,6 +28,7 @@ import ( "regexp" "runtime" "strings" + "time" "github.com/blang/semver" "github.com/docker/machine/libmachine/ssh" @@ -129,6 +130,8 @@ func platform() string { // runStart handles the executes the flow of "minikube start" func runStart(cmd *cobra.Command, args []string) { + defer audit(time.Now()) + register.SetEventLogPath(localpath.EventLog(ClusterFlagValue())) out.SetJSON(outputFormat == "json") diff --git a/cmd/minikube/cmd/start_flags.go b/cmd/minikube/cmd/start_flags.go index 8cd3fdc6acc0..687dc76642bc 100644 --- a/cmd/minikube/cmd/start_flags.go +++ b/cmd/minikube/cmd/start_flags.go @@ -110,6 +110,7 @@ const ( network = "network" startNamespace = "namespace" trace = "trace" + userFlag = "user" ) var ( @@ -156,6 +157,7 @@ func initMinikubeFlags() { startCmd.Flags().StringP(network, "", "", "network to run minikube with. Only available with the docker/podman drivers. If left empty, minikube will create a new network.") startCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "Format to print stdout in. Options include: [text,json]") startCmd.Flags().StringP(trace, "", "", "Send trace events. Options include: [gcp]") + startCmd.Flags().String(userFlag, "", "Sets who will be logged as executing the command, will use os user if none provided") } // initKubernetesFlags inits the commandline flags for Kubernetes related options diff --git a/cmd/minikube/cmd/stop.go b/cmd/minikube/cmd/stop.go index 4fd0fadabc49..3cdf608a39b7 100644 --- a/cmd/minikube/cmd/stop.go +++ b/cmd/minikube/cmd/stop.go @@ -75,6 +75,8 @@ func init() { // runStop handles the executes the flow of "minikube stop" func runStop(cmd *cobra.Command, args []string) { + defer audit(time.Now()) + out.SetJSON(outputFormat == "json") register.Reg.SetStep(register.Stopping) diff --git a/pkg/minikube/out/register/json.go b/pkg/minikube/out/register/json.go index 14f9449666fc..b52ad9de6755 100644 --- a/pkg/minikube/out/register/json.go +++ b/pkg/minikube/out/register/json.go @@ -16,6 +16,8 @@ limitations under the License. package register +import "time" + // PrintStep prints a Step type in JSON format func PrintStep(message string) { s := NewStep(message) @@ -69,3 +71,9 @@ func PrintWarning(warning string) { w := NewWarning(warning) printAndRecordCloudEvent(w, w.data) } + +// RecordAudit records an Audit type in JSON format +func RecordAudit(command string, args string, user string, startTime time.Time, endTime time.Time) { + a := NewAudit(command, args, user, startTime, endTime) + recordCloudEvent(a, a.data) +} diff --git a/pkg/minikube/out/register/log.go b/pkg/minikube/out/register/log.go index 9d340d6b4b2c..8fa51edeac1f 100644 --- a/pkg/minikube/out/register/log.go +++ b/pkg/minikube/out/register/log.go @@ -19,6 +19,7 @@ package register import ( "fmt" "strings" + "time" ) // Log represents the different types of logs that can be output as JSON @@ -154,3 +155,26 @@ func NewErrorExitCode(err string, exitcode int, additionalData ...map[string]str func (s *Error) Type() string { return "io.k8s.sigs.minikube.error" } + +// Audit represents the execution of a command +type Audit struct { + data map[string]string +} + +// Type returns the cloud events compatible type of this struct +func (a *Audit) Type() string { + return "io.k8s.sigs.minikube.audit" +} + +// NewAudit returns a new audit type +func NewAudit(command string, args string, user string, startTime time.Time, endTime time.Time) *Audit { + return &Audit{ + map[string]string{ + "args": args, + "command": command, + "endTime": endTime.String(), + "startTime": startTime.String(), + "user": user, + }, + } +}