Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ go_library(
"//tables/macosrsr",
"//tables/mdm",
"//tables/munki",
"//tables/netskope",
"//tables/networkquality",
"//tables/pendingappleupdates",
"//tables/puppet",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ For production deployment, you should refer to the [osquery documentation](https
| `macos_thermal_pressure` | Reports whether macOS is [thermally throttling](https://developer.apple.com/documentation/foundation/processinfo/thermalstate) the device, via `powermetrics`. Returns `thermal_pressure` (Nominal/Light/Moderate/Heavy/Sleeping) and a derived `is_throttling` integer (1 if not Nominal). | macOS | Use the `interval` constraint to specify sampling duration in milliseconds (default: 1000). Requires root. |
| `mdm` | Information on the device's MDM enrollment | macOS | Code based on work by [Kolide](https://github.com/kolide/launcher). Due to changes in macOS 12.3, the output of `profiles show -type enrollment` can only be generated once a day. If you are running this command with another tool, you should set the `PROFILES_SHOW_ENROLLMENT_CACHE_PATH` environment variable to the path you are caching this. The cache file should be `json` with the keys `dep_capable` and `rate_limited` present, both booleans representing whether the device is capable of DEP enrollment and whether the response from `profiles show -type enrollment` is being rate limited or not. |
| `munki_info` | Information from the last [Munki](https://github.com/munki/munki) run | macOS | Code based on work by [Kolide](https://github.com/kolide/launcher) |
| `netskope` | Status and configuration information from the Netskope client via `nsdiag` | macOS |
| `munki_installs` | Items [Munki](https://github.com/munki/munki) is managing | macOS | Code based on work by [Kolide](https://github.com/kolide/launcher) |
| `network_quality` | Output from the `networkQuality` binary | macOS | This binary is only present on macOS 12 |
| `puppet_facts` | [Puppet](https://puppetlabs.com) facts | Linux / macOS / Windows | |
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.4.1
1.4.2
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/macadmins/osquery-extension/tables/macosrsr"
"github.com/macadmins/osquery-extension/tables/mdm"
"github.com/macadmins/osquery-extension/tables/munki"
"github.com/macadmins/osquery-extension/tables/netskope"
"github.com/macadmins/osquery-extension/tables/networkquality"
"github.com/macadmins/osquery-extension/tables/pendingappleupdates"
"github.com/macadmins/osquery-extension/tables/puppet"
Expand Down Expand Up @@ -102,6 +103,7 @@ func main() {
table.NewPlugin("mdm", mdm.MDMInfoColumns(), mdm.MDMInfoGenerate),
table.NewPlugin("munki_info", munki.MunkiInfoColumns(), munki.MunkiInfoGenerate),
table.NewPlugin("munki_installs", munki.MunkiInstallsColumns(), munki.MunkiInstallsGenerate),
table.NewPlugin("netskope", netskope.NetskopeColumns(), netskope.NetskopeGenerate),
table.NewPlugin("network_quality", networkquality.NetworkQualityColumns(), networkquality.NetworkQualityGenerate),
table.NewPlugin("pending_apple_updates", pendingappleupdates.PendingAppleUpdatesColumns(), pendingappleupdates.PendingAppleUpdatesGenerate),
table.NewPlugin("macadmins_unified_log", unifiedlog.UnifiedLogColumns(), unifiedlog.UnifiedLogGenerate),
Expand Down
25 changes: 25 additions & 0 deletions tables/netskope/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "netskope",
srcs = ["netskope.go"],
importpath = "github.com/macadmins/osquery-extension/tables/netskope",
visibility = ["//visibility:public"],
deps = [
"//pkg/utils",
"@com_github_osquery_osquery_go//plugin/table",
"@com_github_pkg_errors//:errors",
],
)

go_test(
name = "netskope_test",
srcs = ["netskope_test.go"],
embed = [":netskope"],
deps = [
"//pkg/utils",
"@com_github_osquery_osquery_go//plugin/table",
"@com_github_pkg_errors//:errors",
"@com_github_stretchr_testify//assert",
],
)
105 changes: 105 additions & 0 deletions tables/netskope/netskope.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package netskope

import (
"context"
"os"
"regexp"
"strings"

"github.com/macadmins/osquery-extension/pkg/utils"
"github.com/osquery/osquery-go/plugin/table"
"github.com/pkg/errors"
)

const nsdiag = "/Library/Application Support/Netskope/STAgent/nsdiag"

var camelSplit = regexp.MustCompile(`([a-z])([A-Z])`)

func NetskopeColumns() []table.ColumnDefinition {
return []table.ColumnDefinition{
table.TextColumn("orgname"),
table.TextColumn("tenant_url"),
table.TextColumn("addon_host"),
table.TextColumn("addon_checker_host"),
table.TextColumn("gateway"),
table.TextColumn("gateway_ip"),
table.TextColumn("config"),
table.TextColumn("steering_config"),
table.TextColumn("email"),
table.TextColumn("peruser_config"),
table.TextColumn("tunnel_status"),
table.TextColumn("client_status"),
table.TextColumn("dynamic_steering"),
table.TextColumn("on_prem_detection"),
table.TextColumn("explicit_proxy"),
table.TextColumn("tunnel_protocol"),
table.TextColumn("sni_enable"),
table.TextColumn("traffic_mode"),
}
}

func NetskopeGenerate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
r := utils.NewRunner()
fs := utils.OSFileSystem{}
return runNsdiag(r, fs)
}

func runNsdiag(r utils.Runner, fs utils.FileSystem) ([]map[string]string, error) {
row := emptyRow()

_, err := fs.Stat(nsdiag)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return []map[string]string{row}, nil
}
return nil, errors.Wrap(err, "stat nsdiag binary")
}

out, err := r.Runner.RunCmd(nsdiag, "-f")
if err != nil {
return nil, errors.Wrap(err, "run nsdiag")
}
parsed := parseNsdiagOutput(string(out))

for col, val := range parsed {
if _, known := row[col]; known {
row[col] = val
}
}

return []map[string]string{row}, nil
}

func parseNsdiagOutput(output string) map[string]string {
result := make(map[string]string)
for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.SplitN(line, "::", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
val := strings.TrimSpace(parts[1])
val = strings.TrimSuffix(val, ".")
val = strings.TrimSpace(val)
result[keyToColumn(key)] = val
}
return result
}

func keyToColumn(key string) string {
key = camelSplit.ReplaceAllString(key, "${1}_${2}")
key = strings.ReplaceAll(key, " ", "_")
return strings.ToLower(key)
}

func emptyRow() map[string]string {
row := make(map[string]string)
for _, col := range NetskopeColumns() {
row[col.Name] = ""
}
return row
}
Loading