-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7495db1
commit efbbb00
Showing
8 changed files
with
473 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package checkups | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/kolide/launcher/pkg/agent" | ||
"github.com/kolide/launcher/pkg/agent/types" | ||
) | ||
|
||
type bboltdbCheckup struct { | ||
k types.Knapsack | ||
data any | ||
} | ||
|
||
func (c *bboltdbCheckup) Name() string { | ||
return "bboltdb" | ||
} | ||
|
||
func (c *bboltdbCheckup) Run(_ context.Context, _ io.Writer) error { | ||
db := c.k.BboltDB() | ||
if db == nil { | ||
return errors.New("no DB available") | ||
} | ||
|
||
stats, err := agent.GetStats(db) | ||
if err != nil { | ||
return fmt.Errorf("getting db stats: %w", err) | ||
} | ||
|
||
c.data = stats | ||
return nil | ||
} | ||
|
||
func (c *bboltdbCheckup) ExtraFileName() string { | ||
return "" | ||
} | ||
|
||
func (c *bboltdbCheckup) Status() Status { | ||
return Informational | ||
} | ||
|
||
func (c *bboltdbCheckup) Summary() string { | ||
return "N/A" | ||
} | ||
|
||
func (c *bboltdbCheckup) Data() any { | ||
return c.data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package checkups | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"os" | ||
"runtime" | ||
|
||
"github.com/golang-jwt/jwt/v5" | ||
) | ||
|
||
type enrollSecretCheckup struct { | ||
summary string | ||
status Status | ||
} | ||
|
||
func (c *enrollSecretCheckup) Name() string { | ||
return "Enrollment Secret" | ||
} | ||
|
||
func (c *enrollSecretCheckup) Run(_ context.Context, extraFH io.Writer) error { | ||
secretStatus := make(map[string]Status, 0) | ||
secretSummary := make(map[string]string, 0) | ||
|
||
for _, secretPath := range getSecretPaths() { | ||
// Later on, we want to fall back to the _first_ secrets status. Set it here | ||
|
||
st, summary := parseSecret(extraFH, secretPath) | ||
secretStatus[secretPath] = st | ||
secretSummary[secretPath] = summary | ||
|
||
if c.status == Unknown || c.status == "" { | ||
c.status = st | ||
c.summary = summary | ||
} | ||
} | ||
|
||
// Iterate over all the found secrets. If any pass, this passes. Otherwise fall back to the first secret. | ||
// This is kinda roundabout, since this checkup is trying to support multiple possible paths | ||
if c.status == Passing { | ||
return nil | ||
} | ||
for secretPath, status := range secretStatus { | ||
if status == Passing { | ||
c.status = Passing | ||
c.summary = secretSummary[secretPath] | ||
} | ||
} | ||
|
||
if len(secretStatus) < 1 { | ||
c.status = Erroring | ||
c.summary = "No secrets for this platform" | ||
return nil | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c *enrollSecretCheckup) ExtraFileName() string { | ||
return "secret-info.log" | ||
} | ||
|
||
func (c *enrollSecretCheckup) Status() Status { | ||
return c.status | ||
} | ||
|
||
func (c *enrollSecretCheckup) Summary() string { | ||
return c.summary | ||
} | ||
|
||
func (c *enrollSecretCheckup) Data() any { | ||
return nil | ||
} | ||
|
||
// getSecretPaths returns potential platform default secret path. It should probably get folded into flags, but I'm not | ||
// quite sure how yet. | ||
func getSecretPaths() []string { | ||
switch runtime.GOOS { | ||
case "darwin": | ||
return []string{"/etc/kolide-k2/secret"} | ||
case "linux": | ||
return []string{"/etc/kolide-k2/secret"} | ||
case "windows": | ||
return []string{"C:\\Program Files\\Kolide\\Launcher-kolide-k2\\conf\\secret"} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func parseSecret(extraFH io.Writer, secretPath string) (Status, string) { | ||
fmt.Fprintf(extraFH, "%s:\n", secretPath) | ||
defer fmt.Fprintf(extraFH, "\n\n") | ||
|
||
secretFH, err := os.Open(secretPath) | ||
switch { | ||
case os.IsNotExist(err): | ||
fmt.Fprintf(extraFH, "does not exist\n") | ||
return Failing, "does not exist" | ||
case os.IsPermission(err): | ||
fmt.Fprintf(extraFH, "permission denied (might be running as user)\n") | ||
return Informational, "permission denied (might be running as user)" | ||
case err != nil: | ||
fmt.Fprintf(extraFH, "unknown error: %s\n", err) | ||
return Erroring, fmt.Sprintf("unknown error: %s", err) | ||
} | ||
|
||
// If we can read the secret, let's try to extract the munemo | ||
tokenRaw, err := io.ReadAll(secretFH) | ||
if err != nil { | ||
fmt.Fprintf(extraFH, "%s: unable to read: %s\n", secretPath, err) | ||
return Failing, fmt.Sprintf("unable to read: %s", err) | ||
} | ||
|
||
// We do not have the key, and thus CANNOT verify. So this is ParseUnverified | ||
token, _, err := new(jwt.Parser).ParseUnverified(string(tokenRaw), jwt.MapClaims{}) | ||
if err != nil { | ||
fmt.Fprintf(extraFH, "Error parsing JWT:\n%s\n", err) | ||
return Failing, fmt.Sprintf("cannot jwt parse: %s", err) | ||
} | ||
|
||
claims, ok := token.Claims.(jwt.MapClaims) | ||
if !ok { | ||
fmt.Fprintf(extraFH, "no jwt claims\n") | ||
return Failing, "jwt has no claims" | ||
} | ||
|
||
// Print the claims into our extra data | ||
fmt.Fprintf(extraFH, "---\n") | ||
if err := json.NewEncoder(extraFH).Encode(claims); err != nil { | ||
fmt.Fprintf(extraFH, "Cannot json encode: %s\n", err) | ||
} | ||
fmt.Fprintf(extraFH, "---\n") | ||
|
||
// Expect the claims to have an organization | ||
org, ok := claims["organization"] | ||
if !ok { | ||
return Failing, "no organization in claim" | ||
} | ||
|
||
return Passing, fmt.Sprintf("claim for %s", org) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package checkups | ||
|
||
import ( | ||
"archive/zip" | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"io" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
) | ||
|
||
const ( | ||
launchdPlistPath = "/Library/LaunchDaemons/com.kolide-k2.launcher.plist" | ||
launchdServiceName = "system/com.kolide-k2.launcher" | ||
) | ||
|
||
type launchdCheckup struct { | ||
status Status | ||
summary string | ||
} | ||
|
||
func (c *launchdCheckup) Name() string { | ||
if runtime.GOOS != "darwin" { | ||
return "" | ||
} | ||
|
||
return "Launchd" | ||
} | ||
|
||
func (c *launchdCheckup) Run(ctx context.Context, extraWriter io.Writer) error { | ||
// Check that the plist exists (this uses open not stat, because we also want to copy it) | ||
launchdPlist, err := os.Open(launchdPlistPath) | ||
if os.IsNotExist(err) { | ||
c.status = Failing | ||
c.summary = "plist does not exist" | ||
return nil | ||
} else if err != nil { | ||
c.status = Failing | ||
c.summary = fmt.Sprintf("error reading %s: %s", launchdPlistPath, err) | ||
return nil | ||
} | ||
defer launchdPlist.Close() | ||
|
||
extraZip := zip.NewWriter(extraWriter) | ||
defer extraZip.Close() | ||
|
||
zippedPlist, err := extraZip.Create(filepath.Base(launchdPlistPath)) | ||
if err != nil { | ||
c.status = Erroring | ||
c.summary = fmt.Sprintf("unable to create extra information: %s", err) | ||
return nil | ||
} | ||
|
||
if _, err := io.Copy(zippedPlist, launchdPlist); err != nil { | ||
c.status = Erroring | ||
c.summary = fmt.Sprintf("unable to write extra information: %s", err) | ||
return nil | ||
|
||
} | ||
|
||
// run launchctl to check status | ||
var printOut bytes.Buffer | ||
|
||
cmd := exec.CommandContext(ctx, "/bin/launchctl", "print", launchdServiceName) | ||
cmd.Stdout = &printOut | ||
cmd.Stderr = &printOut | ||
if err := cmd.Run(); err != nil { | ||
c.status = Failing | ||
c.summary = fmt.Sprintf("error running launchctl print: %s", err) | ||
return nil | ||
} | ||
|
||
zippedOut, err := extraZip.Create("launchctl-print.txt") | ||
if err != nil { | ||
c.status = Erroring | ||
c.summary = fmt.Sprintf("unable to create launchctl-print.txt: %s", err) | ||
return nil | ||
} | ||
if _, err := zippedOut.Write(printOut.Bytes()); err != nil { | ||
c.status = Erroring | ||
c.summary = fmt.Sprintf("unable to write launchctl-print.txt: %s", err) | ||
return nil | ||
|
||
} | ||
|
||
if !strings.Contains(printOut.String(), "state = running") { | ||
c.status = Failing | ||
c.summary = fmt.Sprintf("state not active") | ||
return nil | ||
} | ||
|
||
c.status = Passing | ||
c.summary = "state is running" | ||
return nil | ||
} | ||
|
||
func (c *launchdCheckup) ExtraFileName() string { | ||
return "launchd.zip" | ||
} | ||
|
||
func (c *launchdCheckup) Status() Status { | ||
return c.status | ||
} | ||
|
||
func (c *launchdCheckup) Summary() string { | ||
return c.summary | ||
} | ||
|
||
func (c *launchdCheckup) Data() any { | ||
return nil | ||
} |
Oops, something went wrong.