├── .gitignore ├── Gopkg.lock ├── Gopkg.toml ├── README.md ├── main.go ├── release.sh └── sample_queries /.gitignore: -------------------------------------------------------------------------------- 1 | osquery-condition 2 | build/ 3 | *.zip 4 | vendor/ 5 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "git.apache.org/thrift.git" 6 | packages = ["lib/go/thrift"] 7 | revision = "0dd823580c78a79ae9696eb9b3650e400fff140f" 8 | 9 | [[projects]] 10 | name = "github.com/Microsoft/go-winio" 11 | packages = ["."] 12 | revision = "78439966b38d69bf38227fbf57ac8a6fee70f69a" 13 | version = "v0.4.5" 14 | 15 | [[projects]] 16 | branch = "master" 17 | name = "github.com/groob/plist" 18 | packages = ["."] 19 | revision = "7b367e0aa692e62a223e823f3288c0c00f519a36" 20 | 21 | [[projects]] 22 | branch = "master" 23 | name = "github.com/kolide/osquery-go" 24 | packages = [".","gen/osquery","transport"] 25 | revision = "03a792fcca9a86c9a717109c2ad8763d0759105b" 26 | 27 | [[projects]] 28 | name = "github.com/pkg/errors" 29 | packages = ["."] 30 | revision = "645ef00459ed84a119197bfb8d8205042c6df63d" 31 | version = "v0.8.0" 32 | 33 | [[projects]] 34 | branch = "master" 35 | name = "golang.org/x/sys" 36 | packages = ["windows"] 37 | revision = "80ad69fa329a43e6f28c61c48325527739bb1560" 38 | 39 | [solve-meta] 40 | analyzer-name = "dep" 41 | analyzer-version = 1 42 | inputs-digest = "6ec855783b758f32fa2a57fd0746f2c9ffbdc5e03b46e48a6bf0d7d4643b478b" 43 | solver-name = "gps-cdcl" 44 | solver-version = 1 45 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | [[constraint]] 2 | branch = "master" 3 | name = "github.com/groob/plist" 4 | 5 | [[constraint]] 6 | branch = "master" 7 | name = "github.com/kolide/osquery-go" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Use `osqueryd` to update the Munki `ConditionalItems.plist`. 2 | You can run the binary as a munki preflight script. 3 | This utility assumes osquery is already running in your environment. 4 | 5 | # Usage 6 | 7 | ``` 8 | Usage of ./osquery-condition: 9 | -queries string 10 | path to line delimited query file 11 | -socket string 12 | path to osqueryd socket (default "/var/osquery/osquery.em") 13 | ``` 14 | 15 | Example: 16 | 17 | ``` 18 | sudo ./osquery-condition -queries ./sample_queries 19 | sudo cat '/Library/Managed Installs/ConditionalItems.plist' 20 | ``` 21 | 22 | # Creating queries 23 | 24 | To create queries for osqueryd to run, write them in a text file, *one line per query*. 25 | Your queries are expected to return key/value pairs as results. 26 | For example, `select * from system_info;` would return a list of key/values. When updating the `ConditionalItems.plist` file, all the keys will be prefixed with `osquery_`. 27 | 28 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "time" 13 | 14 | "github.com/groob/plist" 15 | osquery "github.com/kolide/osquery-go" 16 | "github.com/pkg/errors" 17 | ) 18 | 19 | var version = "dev" 20 | 21 | func main() { 22 | var ( 23 | flQueries = flag.String("queries", "", "path to line delimited query file") 24 | flSocketPath = flag.String("socket", "/var/osquery/osquery.em", "path to osqueryd socket") 25 | ) 26 | flag.Parse() 27 | 28 | if *flQueries == "" { 29 | fmt.Println("No query file specified.") 30 | flag.Usage() 31 | os.Exit(1) 32 | } 33 | 34 | var conditions MunkiConditions 35 | if err := conditions.Load(); err != nil { 36 | if os.IsNotExist(errors.Cause(err)) { 37 | conditions = make(MunkiConditions) 38 | } else { 39 | log.Fatal(err) 40 | } 41 | } 42 | 43 | client, err := osquery.NewClient(*flSocketPath, 10*time.Second) 44 | if err != nil { 45 | fmt.Println("Error creating Thrift client: " + err.Error()) 46 | os.Exit(1) 47 | } 48 | defer client.Close() 49 | 50 | // load queries from list 51 | queries := readQueries(*flQueries) 52 | 53 | // create a (w)rapped client using our OsqueryClient type. 54 | wclient := &OsqueryClient{client} 55 | 56 | resp, err := wclient.RunQueries(queries...) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | 61 | // range over the response channel and format all the responses as 62 | // conditions. 63 | for r := range resp { 64 | for k, v := range r { 65 | conditions[fmt.Sprintf("osquery_%s", k)] = []string{v} 66 | } 67 | } 68 | 69 | if err := conditions.Save(); err != nil { 70 | log.Fatal(err) 71 | } 72 | } 73 | 74 | // read queries from file 75 | func readQueries(path string) []string { 76 | data, err := ioutil.ReadFile(path) 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | lr := bufio.NewReader(bytes.NewReader(data)) 81 | var lines []string 82 | for { 83 | line, _, err := lr.ReadLine() 84 | if err == io.EOF { 85 | break 86 | } 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | lines = append(lines, string(line)) 91 | } 92 | return lines 93 | } 94 | 95 | // OsqueryClient wraps the extension client. 96 | type OsqueryClient struct { 97 | *osquery.ExtensionManagerClient 98 | } 99 | 100 | // RunQueries takes one or more SQL queries and returns a channel with all the responses. 101 | func (c *OsqueryClient) RunQueries(queries ...string) (<-chan map[string]string, error) { 102 | responses := make(chan map[string]string) 103 | 104 | // schedule the queries in a separate goroutine 105 | // it doesn't wait for the responses to return. 106 | go func() { 107 | for _, q := range queries { 108 | resp, err := c.Query(q) 109 | if err != nil { 110 | log.Println(err) 111 | return 112 | } 113 | if resp.Status.Code != 0 { 114 | log.Printf("got status %d\n", resp.Status.Code) 115 | return 116 | } 117 | for _, r := range resp.Response { 118 | responses <- r 119 | } 120 | } 121 | // close the response channel when all queries finish running. 122 | close(responses) 123 | }() 124 | return responses, nil 125 | } 126 | 127 | type MunkiConditions map[string][]string 128 | 129 | func (c *MunkiConditions) Load() error { 130 | f, err := os.Open("/Library/Managed Installs/ConditionalItems.plist") 131 | if err != nil { 132 | return errors.Wrap(err, "load ConditionalItems plist") 133 | } 134 | 135 | if err := plist.NewDecoder(f).Decode(c); err != nil { 136 | return errors.Wrap(err, "decode ConditionalItems plist") 137 | } 138 | return f.Close() 139 | } 140 | 141 | func (c *MunkiConditions) Save() error { 142 | f, err := os.OpenFile("/Library/Managed Installs/ConditionalItems.plist", os.O_CREATE|os.O_WRONLY, 0644) 143 | if err != nil { 144 | return errors.Wrap(err, "open ConditionalItems plist for saving") 145 | } 146 | 147 | enc := plist.NewEncoder(f) 148 | enc.Indent(" ") 149 | if err := enc.Encode(c); err != nil { 150 | return errors.Wrap(err, "encode ConditionalItems plist") 151 | } 152 | return f.Close() 153 | } 154 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="0.1.0" 4 | NAME=osquery-condition 5 | OUTPUT=./build 6 | 7 | echo "Building $NAME version $VERSION" 8 | 9 | mkdir -p ${OUTPUT} 10 | 11 | build() { 12 | echo -n "=> $1-$2: " 13 | GOOS=$1 GOARCH=$2 go build -o ${OUTPUT}/$NAME -ldflags "-X main.version=$VERSION -X main.gitHash=`git rev-parse HEAD`" ./*.go 14 | du -h ${OUTPUT}/${NAME} 15 | } 16 | 17 | build "darwin" "amd64" 18 | zip -r osquery-condition.zip build/ 19 | 20 | -------------------------------------------------------------------------------- /sample_queries: -------------------------------------------------------------------------------- 1 | select * from system_info; 2 | select instance_id, version from osquery_info; 3 | --------------------------------------------------------------------------------