mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
5d35b467d8
Now that these errors are available in log files, we think this adds more confusion for end users.
236 lines
5.7 KiB
Go
236 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
_ "embed"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/pkg/open"
|
|
"github.com/fleetdm/fleet/v4/server/service"
|
|
"github.com/getlantern/systray"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
// version is set at compile time via -ldflags
|
|
var version = "unknown"
|
|
|
|
func main() {
|
|
setupLogs()
|
|
|
|
// Our TUF provided targets must support launching with "--help".
|
|
if len(os.Args) > 1 && os.Args[1] == "--help" {
|
|
fmt.Println("Fleet Desktop application executable")
|
|
return
|
|
}
|
|
log.Info().Msgf("fleet-desktop version=%s", version)
|
|
|
|
devURL := os.Getenv("FLEET_DESKTOP_DEVICE_URL")
|
|
if devURL == "" {
|
|
log.Fatal().Msg("missing URL environment FLEET_DESKTOP_DEVICE_URL")
|
|
}
|
|
deviceURL, err := url.Parse(devURL)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("invalid URL argument")
|
|
}
|
|
|
|
basePath := deviceURL.Scheme + "://" + deviceURL.Host
|
|
deviceToken := path.Base(deviceURL.Path)
|
|
transparencyURL := basePath + "/api/latest/fleet/device/" + deviceToken + "/transparency"
|
|
|
|
onReady := func() {
|
|
log.Info().Msg("ready")
|
|
|
|
systray.SetTemplateIcon(icoBytes, icoBytes)
|
|
systray.SetTooltip("Fleet Desktop")
|
|
|
|
// Add a disabled menu item with the current version
|
|
versionItem := systray.AddMenuItem(fmt.Sprintf("Fleet Desktop v%s", version), "")
|
|
versionItem.Disable()
|
|
systray.AddSeparator()
|
|
|
|
myDeviceItem := systray.AddMenuItem("Initializing...", "")
|
|
myDeviceItem.Disable()
|
|
transparencyItem := systray.AddMenuItem("Transparency", "")
|
|
transparencyItem.Disable()
|
|
|
|
var insecureSkipVerify bool
|
|
if os.Getenv("FLEET_DESKTOP_INSECURE") != "" {
|
|
insecureSkipVerify = true
|
|
}
|
|
rootCA := os.Getenv("FLEET_DESKTOP_FLEET_ROOT_CA")
|
|
|
|
client, err := service.NewDeviceClient(basePath, deviceToken, insecureSkipVerify, rootCA)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("unable to initialize request client")
|
|
}
|
|
|
|
// Perform API test call to enable the "My device" item as soon
|
|
// as the device auth token is registered by Fleet.
|
|
deviceEnabledChan := func() <-chan interface{} {
|
|
done := make(chan interface{})
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(5 * time.Second)
|
|
defer ticker.Stop()
|
|
defer close(done)
|
|
|
|
for {
|
|
_, err := client.ListDevicePolicies()
|
|
|
|
if err == nil || errors.Is(err, service.ErrMissingLicense) {
|
|
myDeviceItem.SetTitle("My device")
|
|
myDeviceItem.Enable()
|
|
transparencyItem.Enable()
|
|
return
|
|
}
|
|
|
|
log.Error().Err(err).Msg("get device URL")
|
|
|
|
<-ticker.C
|
|
}
|
|
}()
|
|
|
|
return done
|
|
}()
|
|
|
|
go func() {
|
|
<-deviceEnabledChan
|
|
tic := time.NewTicker(5 * time.Minute)
|
|
defer tic.Stop()
|
|
|
|
for {
|
|
<-tic.C
|
|
|
|
policies, err := client.ListDevicePolicies()
|
|
switch {
|
|
case err == nil:
|
|
// OK
|
|
case errors.Is(err, service.ErrMissingLicense):
|
|
myDeviceItem.SetTitle("My device")
|
|
continue
|
|
default:
|
|
log.Error().Err(err).Msg("get device URL")
|
|
continue
|
|
}
|
|
|
|
failedPolicyCount := 0
|
|
for _, policy := range policies {
|
|
if policy.Response != "pass" {
|
|
failedPolicyCount++
|
|
}
|
|
}
|
|
|
|
if failedPolicyCount > 0 {
|
|
myDeviceItem.SetTitle(fmt.Sprintf("🔴 My device (%d)", failedPolicyCount))
|
|
} else {
|
|
myDeviceItem.SetTitle("🟢 My device")
|
|
}
|
|
myDeviceItem.Enable()
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-myDeviceItem.ClickedCh:
|
|
if err := open.Browser(deviceURL.String()); err != nil {
|
|
log.Error().Err(err).Msg("open browser my device")
|
|
}
|
|
case <-transparencyItem.ClickedCh:
|
|
if err := open.Browser(transparencyURL); err != nil {
|
|
log.Error().Err(err).Msg("open browser transparency")
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
onExit := func() {
|
|
log.Info().Msg("exit")
|
|
}
|
|
|
|
systray.Run(onReady, onExit)
|
|
}
|
|
|
|
// setupLogs configures our logging system to write logs to rolling files and
|
|
// stderr, if for some reason we can't write a log file the logs are still
|
|
// printed to stderr.
|
|
func setupLogs() {
|
|
stderrOut := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true}
|
|
|
|
dir, err := logDir()
|
|
if err != nil {
|
|
log.Logger = log.Output(stderrOut)
|
|
log.Error().Err(err).Msg("find directory for logs")
|
|
return
|
|
}
|
|
|
|
dir = filepath.Join(dir, "Fleet")
|
|
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
log.Logger = log.Output(stderrOut)
|
|
log.Error().Err(err).Msg("make directories for log files")
|
|
return
|
|
}
|
|
|
|
logFile := &lumberjack.Logger{
|
|
Filename: filepath.Join(dir, "fleet-desktop.log"),
|
|
MaxSize: 25, // megabytes
|
|
MaxBackups: 3,
|
|
MaxAge: 28, // days
|
|
}
|
|
|
|
log.Logger = log.Output(zerolog.MultiLevelWriter(
|
|
zerolog.ConsoleWriter{Out: logFile, TimeFormat: time.RFC3339Nano, NoColor: true},
|
|
stderrOut,
|
|
))
|
|
}
|
|
|
|
// logDir returns the default root directory to use for application-level logs.
|
|
//
|
|
// On Unix systems, it returns $XDG_STATE_HOME as specified by
|
|
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
|
|
// non-empty, else $HOME/.local/state.
|
|
// On Darwin, it returns $HOME/Library/Logs.
|
|
// On Windows, it returns %LocalAppData%
|
|
//
|
|
// If the location cannot be determined (for example, $HOME is not defined),
|
|
// then it will return an error.
|
|
func logDir() (string, error) {
|
|
var dir string
|
|
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
dir = os.Getenv("LocalAppData")
|
|
if dir == "" {
|
|
return "", errors.New("%LocalAppData% is not defined")
|
|
}
|
|
|
|
case "darwin":
|
|
dir = os.Getenv("HOME")
|
|
if dir == "" {
|
|
return "", errors.New("$HOME is not defined")
|
|
}
|
|
dir += "/Library/Logs"
|
|
|
|
default: // Unix
|
|
dir = os.Getenv("XDG_STATE_HOME")
|
|
if dir == "" {
|
|
dir = os.Getenv("HOME")
|
|
if dir == "" {
|
|
return "", errors.New("neither $XDG_STATE_HOME nor $HOME are defined")
|
|
}
|
|
dir += "/.local/state"
|
|
}
|
|
}
|
|
|
|
return dir, nil
|
|
}
|