Exploring Osquery, Kolide’s Fleet and Graylog for Endpoint Visibility


  • Desire for fleet visibility
  • Visibility on both clients and hosts
  • Ability to be alerted if something changes, but not necessarily enforce
  • There’s one of me
  • Easy to stand up
  • Easy to maintain
  • Automation
  • As much as I wish I had time to “dev” it- I just need it to work.

This stack

There’s a lot of bias here, but again I wanted it to be similar to what I am already doing, and as much as possible fit into infrastructure I have already running.

The pieces…


What is osquery? (abridged)

osquery exposes an operating system as a high-performance relational database. This allows you to write SQL-based queries to explore operating system data. With osquery, SQL tables represent abstract concepts such as running processes, loaded kernel modules, open network connections, browser plugins, hardware events or file hashes.

SQL tables are implemented via a simple plugin and extensions API. A variety of tables already exist and more are being written: https://osquery.io/schema. To best understand the expressiveness that is afforded to you by osquery, consider the following SQL queries:

List the users:

SELECT * FROM users;

Check the processes that have a deleted executable:

SELECT * FROM processes WHERE on_disk = 0;

Get the process name, port, and PID, for processes listening on all interfaces:

SELECT DISTINCT processes.name, listening_ports.port, processes.pid
FROM listening_ports JOIN processes USING (pid)
WHERE listening_ports.address = '';

Find every OS X LaunchDaemon that launches an executable and keeps it running:

SELECT name, program || program_arguments AS executable
FROM launchd
WHERE (run_at_load = 1 AND keep_alive = 1)
AND (program != '' OR program_arguments != '');

A note on osqueryi vs osqueryd

osqueryi is the osquery interactive query console/shell. It is completely standalone and does not communicate with a daemon and does not need to run as an administrator. Use the shell to prototype queries and explore the current state of your operating system.


osqueryd is the host monitoring daemon that allows you to schedule queries and record OS state changes. The daemon aggregates query results over time and generates logs, which indicate state change according to each query. The daemon also uses OS eventing APIs to record monitored file and directory changes, hardware events, network events, and more.

Fleet by Kolide

So now we know just a little bit of what osquery can do, so how canwe automate that? Make that work for use en mass?

Thats where Fleet by Kolide comes in… Kolide offers another product as a SaaS option, Kolide Cloud:

Kolide Cloud is the fastest way to get started with Osquery in your organization. Following our setup guide, you can have Kolide on your machine and reporting insights in less than two minutes flat. Our pre-built packages make organization-wide deployment a piece of cake with the tools you already use today.

Understanding Fleet

Fleet will:
– hold our client reported information
– queue and send queries to clients/servers
– fleet will log any findings

Installing Fleet

NOTE: You can also just use the Quickstart method, and skip to Exploring Fleet

Github Docs


If you want to use Docker, you can pull the latest Fleet docker image:
docker pull kolide/fleet


If want to use a linux vm or otherwise, here is an example of an Ubuntu 16.04 setup process, after your initial vm setup.

Get the latest binary, move it to /usr/bin as fleet

$ wget https://dl.kolide.co/bin/fleet_latest.zip
$ unzip fleet_latest.zip 'linux/*' -d fleet
$ sudo apt install unzip
$ sudo cp fleet/linux/fleet_linux_amd64 /usr/bin/fleet

Installing MySQL:

$ sudo apt-get install mysql-client

You can run this db local or on a remote db server thats up to you, for sake of illustration, lets say tou want to run it on a remote db server…

On your remote db server:

$ mysql -u root -p
mysql> create database fleet;
create user 'fleet'@'$FLEET_IP' identified by '$PSW';
create user 'fleet'@'$FLEET_FQDN' identified by '$PSW';
grant all on fleet.* to 'fleet'@'$FLEET_IP';
grant all on fleet.* to 'fleet'@'$FLEET_FQDN';
flush privileges;
mysql> flush privileges;
mysql> select user,host from mysql.db where db='fleet';
| user | host |
| fleet | $FLEET_IP |
| fleet | $FLEET_FQDN |
2 rows in set (0.00 sec)
mysql> exit;

So now we can prepare the fleet database via the binary we installed on the fleet host:

/usr/bin/fleet prepare db \ --mysql_address=your.database.com:port \
--mysql_database=fleet --mysql_username=fleet \ # or whatever was created
--mysql_password='$PSW' # or prompt for input

This will prepare the db as needed, once this is done, you could serve fleet service via cli such as:

/usr/bin/fleet serve \
--mysql_address=your.database.com:port \ --mysql_database=fleet --mysql_username=fleet \ --mysql_password='CiKv_Gzy9:>B]vJ-jifsm&' \ --redis_address= \ --server_cert=/your/host/Certs/fleet.pem \ --server_key=/your/host/Certs/fleet.key \ --logging_json --auth_jwt_key $auth_jwt_key

But fleet can also take a kolide.yaml config file, docs, so above becomes:

address: your.database.com:port
database: fleet
username: fleet
password: $PSW
cert: /your/host/Certs/fleet2.pem
key: /your/host/Certs/fleet2.key
jwt_key: $auth_jwt_key
status_log_file: /var/log/osquery/status.log
result_log_file: /var/log/osquery/result.log
label_query_update_interval: 1h
enable_log_rotation: true
json: false
debug: false

I won’t get into all of Fleets options, but most are the above are self explanatory.

Now we could launch fleet as such:

/usr/bin/fleet serve --config /your/config/kolide.yaml

But I’d rather have it be controlled through systemd, launch automatically etc, so before we launch we can configure systemd

Lets create /etc/systemd/system/fleet.service

Description=Fleet web service

ExecStart=/usr/bin/fleet serve --config /your/config/kolide.yaml


As well I edited rsyslog.d to send service logs to a stand alone file, /var/log/fleet/fleet-out.log dump them w/o sending to syslog, appending or created an rsyslog.d conf with the follow lines should ensure that:

if $programname == 'Fleet' then /var/log/fleet/fleet-out.log
if $programname == 'Fleet' then ~

We can enable this service to be started at boot, if desired:

systemctl enable fleet.service

And now we can start the service with:

systemctl start fleet.service

As well get systemctl information on the server:

$ systemctl status fleet.service
● fleet.service - Fleet web service
Loaded: loaded (/etc/systemd/system/fleet.service; enabled; vendor preset: enabled)
Active: active (running) since ... ; x days ago
Main PID: 8722 (fleet)
Tasks: 11
Memory: 546.1M
CPU: 17min 39.910s
CGroup: /system.slice/fleet.service
└─8722 /usr/bin/fleet serve --config /your/config/kolide.yaml

Fleet service handles the rotation of the logs as well, no need to worry about implementing additional logrotate

Exploring Fleet

For testing/demo, I recommend checking out Fleet via Docker which is found on Kolide’s Fleet “Quickstart” Guide.
It can be as simple as

git clone https://github.com/kolide/kolide-quickstart.git
./demo.sh up

and is well documented.

Enrolling Clients with Fleet


If you chose to do the Quickstart, you can run this from the cloned directory:
./demo.sh enroll mac which will generate a macOS installer package into ../out.

If you are running another iteration, perhaps more production, I encourage you to take a look at Kolide/Launcher for an osquery management tool…

Keep in mind the docker instance is bound to localhosts so demo clients need to be configured as such…

What this package does is install a few things:
– LaunchDaemon
– starts osqueryd on your system
– allows for tls communication
– will be self-signed in demo mode
– file containing secret
– allows for the client enrollment
kolide.flags file
– list of preferences / parameters

NOTE: Fleet just configures kolide.flags based on osqueryd‘s built in clip-flags to communicate with the server, (which spec is outlined here).


If you decide to just bootstrap for funsies or demo via the Quickstart we can get this bootsrapped on 16.04 by…

You can install osquery with apt its required to be installed before its configured with the kolide.flags

export OSQUERY_KEY=1484120AC4E9F8A1A577AEEE97A80C63C9D8B80B
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $OSQUERY_KEY
sudo add-apt-repository 'deb [arch=amd64] https://pkg.osquery.io/deb deb main'
sudo apt-get update
sudo apt-get install osquery -y

As with the macOS installer we need all the same parts,

You can manually put these into /etc/osquery/

And then manually kick of osqueryd with this command:
/usr/bin/osqueryd --flagfile=/etc/osquery/kolide.flags

You should now see your clients in the server if the config was successful.

Fleet Documentation

#kolide in osquery Slack
API Documentation
Github kolide/fleet


A little out of scope here, but there are loads of options and plenty of good docs out there:
Graylog Downloads, (OVA, Open Stack, EC2, Docker, RPM, DEB
Scripts, Orchestration, etc )
Graylog Documentation
– Of which I highly recommend reviewing Architectural considerations before moving towards a production environment
– That being said the OVA is a great quick start option!

Tips on ingesting osquery logs into Graylog

On our Fleet sever config.yaml we designated the following logs:
status_log_file: /var/log/osquery/status.log
result_log_file: /var/log/osquery/result.log

Now we can setup sending those to Graylog.

I much prefer to send logs to individual files and set rules surrounding them individually. And keeping them separate from the syslog

If you chose to send those logs to syslog this portion may vary for you…

You can do this a couple different ways,

A) rsyslog

You could set up a rsyslog entry for that log and send it that way-

B) agent, ie Filebeat by Elastic

Filebeat is a tool from Elastic that allows for managing log forwarding for multiple files, even parsing log files, see the Filebeat Documentation for more…

Let me explain why I like B, primarily because I can deploy a config that manages all the needed settings for multiple log files, and its as simple as:

– input_type: log
– /var/log/osquery/status.log
– /var/log/osquery/result.log

# output
hosts: ["your.graylog.com:PORT"]

#logging info

logging.level: info
logging.to_files: true
logging.to_syslog: false
path: /var/log/Filebeat/
name: filebeat.log
keepfiles: 7

This config will watch just the listed log files, as well as log its doings to /var/log/Filebeat/filebeat.log. One BIG bonus I like about Filebeat is that its aware of what it has sent and hasn’t…

In any environment, application downtime is always lurking on the edges. Filebeat reads and forwards log lines and — if interrupted — remembers the location of where it left off when everything is back online. filebeat

So if your syslog is down, or overloaded, and a successful log forward is not sent, then it knows and “catches up” later.

Once Filebeat is setup, you can configure an input for it on Graylog. Detailed info over at Graylog: Inputs

Once you are on youre inputs page, select Beats option:

This will bring up a config window, update as needed:

Additionally once the input is configured, “Manage Extractor”:

And find an example log file, (you may have to come back to this part), and load it-
Select the message portion of the log, and then select Select extractor type which is JSON and that should be it!

The message block received will look like this:

{"name":"pack\/Example Check\/etc_hosts","hostIdentifier":"macOS","calendarTime":"Fri Apr 27 20:38:36 2018 UTC","unixTime":"1524861516","epoch":"0","counter":"0","decorations":{"host_uuid":"564D4617-52F7-6924-9698-05E433322827","hostname":"macOS"},"columns":{"address":"","hostnames":"whatever"},"action":"added"}

Now will be converted to:

Fri Apr 27 20:38:36 2018 UTC
{"name":"pack\/Example Check\/etc_hosts","hostIdentifier":"macOS","calendarTime":"Fri Apr 27 20:38:36 2018 UTC","unixTime":"1524861516","epoch":"0","counter":"0","decorations":{"host_uuid":"564D4617-52F7-6924-9698-05E433322827","hostname":"macOS"},"columns":{"address":"","hostnames":"whatever"},"action":"added"}
pack/Example Check/etc_hosts

And once an item is extracted as such, each of these keys will be stand alone inside Graylog. Allowing you to alert, sort and graph based on their contents.

Monitoring and Alerting with Graylog

Again we are quickly approaching scope creep for this session, Graylog has great docs and here are some good places to start:

Graylog Streams
Graylog Alerts



osquery Docs
osquery tables
Github facebook/osquery
osquery Slack


#kolide in osquery Slack
API Documentation
Github kolide/fleet



Munki, Gitlab & Git-LFS

NOTE: This is not a HOW TO, more of a rant, the how to that may or may not come later. This is written from the perspective of someone who manages (for the most part) their infrastructure from end to end, vm config, service management, monitoring and patching. When it comes to scale, and complexity your milage may very.


  • there are some caveats this route
  • its worth it
  • git isn’t a one way street, you can use it as little or as much as you want
  • Gitlab as a tool sets you up for more in the CI/CD realm

The idea

This post is just some gernal process / lessons learned from migrating a Munki repo to a git tracked repository

fwiw, “git” and “git-lfs” don’t need Gitlab, its just my life, and these are my thoughts.


  • vcs
    • a version control system was huge, the benefit to see what I changed when I changed was really rad
  • pipelines
    • though I didn’t have that word for it at the time, some flow of dev/test/deploy of a repo
  • multiple users
    • though this still isn’t prod, I wanted the ability for multiple users to make changes, and said changes be tracked
  • no more smb/sfp
    • all the transport security can be done with pub/priv keys, etc

Why Gitlab?

  • I could keep it locally
    • OR use the cloud, privately too
  • The CE edition is rad (and I will do my best to link exclusively to the Community Edition docs)
    • and free
  • It has CI/CD built in
    • (though I didn’t realize how great this was ’till later)

The Reality

There were some kinks that had to be worked out, the first and maybe most obvious was managing large files with git. Gitlab has LFS support built in, which is rad, but still comes with its own nuances.


Git Large File Storage

Git Large File Storage (LFS) replaces large files such as audio samples, videos, datasets, and graphics with text pointers inside Git, while storing the file contents on a remote server like GitHub.com or GitHub Enterprise.

Heres a handy tutorial: Getting started with Git LFS

Git LFS on Gitlab

Sounds great, and can be enabled in Gitlab easily.

LFS on Gitlab also requires you use HTTPS for auth and transport, rather than ssh. Digging into the Gitlab administration docs, we can see the docs straight list some pretty big limitations:

Support for removing unreferenced LFS objects was added in 8.14 onwards.
LFS authentications via SSH was added with GitLab 8.12
Only compatible with the GitLFS client versions 1.1.0 and up, or 1.0.2.
The storage statistics currently count each LFS object multiple times for every project linking to it

But some stuff they don’t tell you that I found out the hard way-

LFS caches (this, srsly)

What I found was when Gitlab was receiving LFS file sit could cache them in a /cache location, then move them to the configured storage location.

This was noticed when my OS disk on my VM filled. womp. Modifying the gitlab.rb you can change the LFS storage location.

Looking at gitlab.rb.template, we have these LFS options:

### Git LFS
# gitlab_rails['lfs_enabled'] = true
# gitlab_rails['lfs_storage_path'] = "/var/opt/gitlab/gitlab-rails/shared/lfs-objects"
# gitlab_rails['lfs_object_store_enabled'] = false # EE only
# gitlab_rails['lfs_object_store_direct_upload'] = false
# gitlab_rails['lfs_object_store_background_upload'] = true
# gitlab_rails['lfs_object_store_proxy_download'] = false
# gitlab_rails['lfs_object_store_remote_directory'] = "lfs-objects"

Oh nice! a gitlab_rails['lfs_storage_path'] option, sweet, so you can store your repo on /dev/sd(whatever), this is good to know- so say- your os disk doesn’t fill…

And what about your client? Most people have Munki running with autopkg or munki admin on a macOS box. So you have to have Git-LFS on your mac, and little less supported.

LFS on macOS

You can do this via the installer git provides (macOS), which is basically the command line extension and a shell script. Or you could use brew, loads of people have loads of opinions about brew, so don’t use it and just keep that to yourself.

Regardless you will need to initialize it-

# Update global git config
$ git lfs install
# Update system git config
$ git lfs install --system

Which is like HEY GIT we going to use LFS now. But for what? and when?, touché git.

Looking into Configuring Git Large File Storage you can, once you’re in a tracked dir,

$  git lfs track "*.psd"
Adding path *.psd

Which is cool, and it gets added to your .gitattributes file, but most admins know what they’re big files in their munki repo are… so somehting like this in your .gitattributes file may be more applicable:

*.pkg filter=lfs diff=lfs merge=lfs -text
*.mpkg filter=lfs diff=lfs merge=lfs -text
*.dmg filter=lfs diff=lfs merge=lfs -text
It still CACHES

Lets take a look at our mcOS git env…

$ git lfs env

That effectively means your local repo folder can be double its actual size. So plan accordingly.

Once its tracked it is as simple as a git push to get those files and changes to the Gitlab server.


I know it sounds pretty negative up until this point, the benefit though, of the hump of getting it setup is git. And you can “git” as much or as little as you want.

Munki & Git

Theres some great documentation on that here. But specifically check out Munki’s Repo Plugins, specifically the GitFileRepo:

GitFileRepo – a proof-of-concept/demonstration plugin. It inherits the behavior of FileRepo and does git commits for file changes in the repo (which must be already configured/initialized as a git repo)

This is rad because once your repo is tracked it does git commits for file changes in the repo. Rad.

Git Theories

master Branch All Day

Once you have all your stuff in git, you can choose how you’re going to use it- I have seen a lot of benefit of a tracked repo that just simply commits changes to master. Since dev, test, and prod are all contained in “munki logic” commit any changes to master allows you to track any changes made to .pkginfo or manifest files.

Which this works, and is totally legit, and will get you a load of good info from tracked files.

Let’s Face(book) It, we need more

Or maybe you don’t but Facebook’s CPE team has a really rad option…

Check out their CPE resources, specifically the autopkg_tools

This is an AutoPkg wrapper script creates a separate git feature branch and puts up a commit for each item that is imported into a git-managed Munki repo.

They have a Getting Started Guide if this sounds like more of what you’re looking for-

Stuff I didn’t touch on, that I could

  • catalogs, not tracking them, making them, making them with a runner
  • git-fat as an option?

In summary

I personally won’t go back, there was a little tweaking to get it all sorted, but the information and tracking git provides to the munki repo is well worth it.

Coming Soon

  • I am going to try and sanitize some of the helpful CI/CD stuff I got rolling in Gitlab and talk about it here.
  • Munki in the cloud stuff
  • macOS monitoring?
  • maybe a similar rant on osquery and fleet.io


Macadmin Resources

🎥 Mac Justice – Intro to Gitlab (MacDevOPs, shorter)
🎥 Mac Justice – Intro to Gitlab (PSU Macadmins, longer)

🔗 Advanced Munki Infrastructure: Moving to Cloud Services by Rick Heil
🎥 Advanced Munki Infrastructure: Moving to Cloud Services

General Resources

🔗 Git LFS
🔗 Gitlab CE
🔗 Gitlab and LFS

🎥 Git Large File Storage – How to Work with Big Files
🎥 Git LFS Training – GitHub Universe 2015
🎥 Tracking huge files with Git LFS, GlueCon 2016

Munkireport-PHP on Ubuntu 16.04 w/ SQL


After deciding Docker wasn’t a direction I wanted to head infrastructure wise I decided to pursue Ubuntu host… but I also wanted to update the infrastructure, so I decided to pursue an option like this:

  • Ubuntu 16.o4
  • PHP 7
  • Non-local SQL

I’ve cited him once and I’ll do it again, Clayton Burlison has a great blog post on such a thing for Ubuntu 14.04 (less non-local db)which was the basic outline I used to move forward with this project.

Continue reading “Munkireport-PHP on Ubuntu 16.04 w/ SQL”

Munki, Docker and why you’d want to even try. The video!

Here  is a video from PSU MacAdmins where I take a high level look at Munki, Docker and why you’d want to even try to get them to play nice with one another- or what better options for hosting your repo may be…


I love what the folks on the PSU MacAdmins team have been doing for the community, you can read more about them and the PSU MacAdmins conference here.

Munkireport-PHP on Docker

In my previous post I delved into getting a munki repo with ssl client-server cert protecting up and running on a docker host. You can read that here.

So the next step I wanted to get up and running next to it was the great tool Munkireport-PHP.  More than likely if you are looking into docker as an option for a munki repo server you have some experience with munkireport-php as a reporting tool for your fleet. I am not endorsing this as the only way or even a preferred way- I just wanted to see what it looked like to get it up and running on a docker host- and it was super easy (in comparison to getting a munki repo configured and up and running).

Continue reading “Munkireport-PHP on Docker”

Penn State Mac Admins Conference

I will be presenting the MacAdmins Conference at Penn State this summer with my good friend and colleague Sean Kaiser find more about him here. I will be heading up Life In A Post Xserve World and Sean 2 years after 1400+ computers in 3 weeks. Are we still nuts?. Here is a full list of topics and speakers. Looking forward to being inducted into a great community, and hope I can give just the slightest amount of wisdom back.