/
run.go
156 lines (128 loc) · 4.45 KB
/
run.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package cli
import (
"io"
"os"
"runtime"
"github.com/alecthomas/kong"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/immune-gmbh/agent/v3/pkg/core"
"github.com/immune-gmbh/agent/v3/pkg/state"
"github.com/immune-gmbh/agent/v3/pkg/tui"
"github.com/immune-gmbh/agent/v3/pkg/util"
)
const (
programName = "guard"
programDesc = "immune Guard command-line utility"
)
type verboseFlag bool
func (v verboseFlag) BeforeApply() error {
log.Logger = log.Level(zerolog.DebugLevel)
return nil
}
type traceFlag bool
func (v traceFlag) BeforeApply() error {
log.Logger = log.Logger.With().Caller().Logger().Level(zerolog.TraceLevel)
return nil
}
type rootCmd struct {
// Global options
StateDir string `name:"state-dir" default:"${state_default_dir}" help:"Directory holding the cli state" type:"path"`
LogFlag bool `name:"log" help:"Force log output on and text UI off"`
Verbose verboseFlag `help:"Enable verbose mode, implies log"`
Trace traceFlag `hidden:""`
Colors bool `help:"Force colors on for all console outputs (default: autodetect)"`
// Subcommands
Attest attestCmd `cmd:"" help:"Attests platform integrity of device"`
Enroll enrollCmd `cmd:"" help:"Enrolls device at the immune SaaS backend"`
Collect collectCmd `cmd:"" help:"Only collect firmware data"`
}
func initUI(forceColors bool, forceLog bool) io.Writer {
notty := os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
// honor NO_COLOR env var as per https://no-color.org/ like the colors library we use does, too
_, noColors := os.LookupEnv("NO_COLOR")
cw := zerolog.ConsoleWriter{
Out: nil,
NoColor: false,
TimeFormat: "15:04:05"}
// handle different console environments
// if tui is disabled, then the log is our ui; so we use stdout
cw.NoColor = (noColors || notty) && !forceColors
if cw.NoColor {
cw.Out = os.Stdout
} else {
cw.Out = colorable.NewColorableStdout()
}
// use tui instead of log as ui
if !forceLog && !notty {
tui.Init(cw.NoColor)
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
cw.Out = tui.Err
}
// apply settings to global default logger
log.Logger = log.Output(cw)
return cw
}
func RunCommandLineTool() int {
agentCore := core.NewCore()
// add info about build to description
desc := programDesc + " " + *agentCore.ReleaseId + " (" + runtime.GOARCH + ")"
// set global log level to trace so individual loggers can be set to all levels
zerolog.SetGlobalLevel(zerolog.TraceLevel)
// set default global logger log level before kong possibly overrides it
log.Logger = log.Level(zerolog.InfoLevel)
// Dynamically build Kong options
options := []kong.Option{
kong.Name(programName),
kong.Description(desc),
kong.UsageOnError(),
kong.ConfigureHelp(kong.HelpOptions{
Compact: true,
Summary: true,
}),
kong.Vars{
// setting the TPM default path here is incompatible with future cross-platform client/server agent connections
"tpm_default_path": state.DefaultTPMDevice(),
"state_default_dir": state.DefaultStateDir(),
},
}
options = append(options, osSpecificCommands()...)
// hide --standalone when not on windows; only windows supports connecting to agent service
if runtime.GOOS != "windows" {
options = append(options, kong.IgnoreFields("Standalone"))
}
// Parse common cli options
var cli rootCmd
ctx := kong.Parse(&cli, options...)
// init UI and determine a std log output for logging from remote agents
// when running as svc client we don't want tui b/c the tui states are not transmitted
runSvcClient := runtime.GOOS == "windows" && !cli.Attest.Standalone && !cli.Enroll.Standalone
stdLogOut := initUI(cli.Colors, cli.LogFlag || bool(cli.Verbose) || bool(cli.Trace) || runSvcClient)
// tell who we are
log.Debug().Msg(desc)
// bail out if not root
root, err := util.IsRoot()
if err != nil {
log.Warn().Msg("Can't check user. It is recommended to run as administrator or root user")
log.Debug().Err(err).Msg("util.IsRoot()")
} else if !root {
tui.SetUIState(tui.StNoRoot)
log.Error().Msg("This program must be run with elevated privileges")
return 1
}
// init agent core
if err := agentCore.Init(cli.StateDir, &log.Logger); err != nil {
core.LogInitErrors(&log.Logger, err)
tui.DumpErr()
return 1
}
// Run the selected subcommand
if err := ctx.Run(agentCore, &stdLogOut); err != nil {
tui.DumpErr()
return 1
} else {
return 0
}
}