Add PoC autoupdate functionality

This demonstrates the ability to download the verified "stable" version
of osquery and execute it with the given flags.
This commit is contained in:
Zach Wasserman 2021-01-12 19:00:16 -08:00
parent 0af9b67d89
commit 501e404f5e
12 changed files with 450 additions and 326 deletions

11
go.mod
View File

@ -3,14 +3,15 @@ module github.com/fleetdm/orbit
go 1.15
require (
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.2
github.com/golang/protobuf v1.3.4 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/oklog/run v1.1.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.6.1
github.com/theupdateframework/notary v0.6.2-0.20200804143915-84287fd8df4f
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55
github.com/urfave/cli/v2 v2.3.0
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e // indirect
)

141
go.sum
View File

@ -1,15 +1,16 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 h1:iPf1jQ8yKTms6k6L5vYSE7RZJpjEe5vLTOmzRZdpnKc=
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -17,130 +18,110 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/go v1.5.1-1 h1:hr4w35acWBPhGBXlzPoHpmZ/ygPjnmFVxGxxGnMyP7k=
github.com/docker/go v1.5.1-1/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz+cjWoom6l0=
github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
github.com/theupdateframework/notary v0.6.2-0.20200804143915-84287fd8df4f h1:myKguilK7Xy8V5sjfJ8CYn2cD/aRlDFO4hzkqZ9HhgQ=
github.com/theupdateframework/notary v0.6.2-0.20200804143915-84287fd8df4f/go.mod h1:VmySTua0RaZOe78Zx4/i3bCl9eNs0UvBOPV+1ps9t6U=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw=
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug=
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55 h1:Zn+mA4qTRyao2Petd+YovKaFOUuxDj158kqCIqvwTow=
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55/go.mod h1:L+uU/NRFK/7h0NYAnsmvsX9EghDB5QVCcHCIrK2h5nw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

89
main.go
View File

@ -6,11 +6,14 @@ import (
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/fleetdm/orbit/pkg/constant"
"github.com/fleetdm/orbit/pkg/database"
"github.com/fleetdm/orbit/pkg/insecure"
"github.com/fleetdm/orbit/pkg/osquery"
"github.com/fleetdm/orbit/pkg/update"
"github.com/fleetdm/orbit/pkg/update/badgerstore"
"github.com/oklog/run"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
@ -18,7 +21,7 @@ import (
const (
serverURL = "localhost:8080"
notaryURL = "https://localhost:4443"
notaryURL = "https://tuf.fleetctl.com"
certPath = "/tmp/fleet.pem"
)
@ -26,7 +29,7 @@ func main() {
app := cli.NewApp()
app.Name = "Orbit osquery"
app.Usage = "A powered-up, (near) drop-in replacement for osquery"
defaultRootDir := "/usr/local/orbit"
defaultRootDir := "/usr/local/fleet"
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "root-dir",
@ -53,59 +56,37 @@ func main() {
},
}
app.Action = func(c *cli.Context) error {
// err := initialize(c)
// if err != nil {
// return errors.Wrap(err, "initialize")
// }
if err := os.MkdirAll(c.String("root-dir"), constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "initialize root dir")
}
// rootDir := ".trust"
// if err := os.MkdirAll(rootDir, 0700); err != nil {
// panic(err)
// }
// server := "https://localhost:4443"
// image := "example.com/collection"
// transport := http.DefaultTransport.(*http.Transport).Clone()
// transport.TLSClientConfig = &tls.Config{
// InsecureSkipVerify: true,
// }
// repo, err := client.NewFileCachedRepository(
// rootDir,
// data.GUN(image),
// server,
// transport,
// nil,
// trustpinning.TrustPinConfig{},
// )
// if err != nil {
// panic(err)
// }
// targets, err := repo.ListTargets()
// if err != nil {
// panic(err)
// }
// for _, tgt := range targets {
// fmt.Printf("%s\t%s\n", tgt.Name, hex.EncodeToString(tgt.Hashes["sha256"]))
// }
// tgt, err := repo.GetTargetByName("LICENSE")
// if err != nil {
// panic(err)
// }
// fmt.Printf("%+v\n", tgt)
updater, err := update.New(update.Options{
RootDirectory: c.String("root-dir"),
ServerURL: c.String("notary-url"),
InsecureTransport: true,
})
db, err := database.Open(filepath.Join(c.String("root-dir"), "orbit.db"))
if err != nil {
return err
}
defer func() {
if err := db.Close(); err != nil {
log.Printf("Error closing badger: %v", err)
}
}()
opt := update.DefaultOptions
opt.RootDirectory = c.String("root-dir")
opt.ServerURL = c.String("notary-url")
opt.LocalStore = badgerstore.New(db.DB)
updater, err := update.New(opt)
if err != nil {
return err
}
if err := updater.UpdateMetadata(); err != nil {
return err
}
log.Println(updater.Targets())
osquerydPath, err := updater.Get("osqueryd", "macos", "stable")
if err != nil {
return err
}
fmt.Println(updater.Lookup("test", "LICENSE"))
_ = updater
var g run.Group
var options []func(*osquery.Runner) error
@ -146,9 +127,9 @@ func main() {
)
}
if enrollSecret := c.String("enroll_secret"); enrollSecret != "" {
if enrollSecret := c.String("enroll-secret"); enrollSecret != "" {
options = append(options,
osquery.WithEnv([]string{"ENROLL_SECRET="}),
osquery.WithEnv([]string{"ENROLL_SECRET=" + enrollSecret}),
osquery.WithFlags([]string{"--enroll_secret_env", "ENROLL_SECRET"}),
)
}
@ -163,6 +144,8 @@ func main() {
osquery.WithFlags([]string{"--verbose"}),
)
options = append(options, osquery.WithPath(osquerydPath))
// Create an osquery runner with the provided options
r, _ := osquery.NewRunner(options...)
g.Add(r.Execute, r.Interrupt)

View File

@ -9,7 +9,7 @@ import (
"testing"
"time"
"github.com/bmizerany/assert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@ -5,4 +5,7 @@ const (
DefaultDirMode = 0o700
// DefaultFileMode is the default file mode to apply to created files.
DefaultFileMode = 0o600
// DefaultExecutableMode is the default file mode to apply to created
// executable files.
DefaultExecutableMode = 0o700
)

View File

@ -59,13 +59,21 @@ func WithEnv(env []string) func(*Runner) error {
}
}
func WithPath(path string) func(*Runner) error {
return func(r *Runner) error {
r.cmd.Path = path
return nil
}
}
func (r *Runner) Execute() error {
ctx, cancel := context.WithCancel(context.Background())
r.cancel = cancel
if err := r.proc.Start(); err != nil {
return errors.Wrap(err, "start osquery")
}
ctx, cancel := context.WithCancel(context.Background())
r.cancel = cancel
if err := r.proc.StopOrKill(ctx, 10*time.Second); err != nil {
return errors.Wrap(err, "osquery exited with error")
}

View File

@ -0,0 +1,69 @@
// package badgerstore implements the go-tuf LocalStore interface using Badger
// as a backing store.
package badgerstore
import (
"encoding/json"
"strings"
"github.com/dgraph-io/badger/v2"
"github.com/theupdateframework/go-tuf/client"
)
const (
keyPrefix = ":tuf-metadata:"
)
type badgerStore struct {
db *badger.DB
}
// New creates the new store given the badger DB instance.
func New(db *badger.DB) client.LocalStore {
return &badgerStore{db: db}
}
// SetMeta stores the provided metadata.
func (b *badgerStore) SetMeta(name string, meta json.RawMessage) error {
if err := b.db.Update(func(tx *badger.Txn) error {
if err := tx.Set([]byte(keyPrefix+name), meta); err != nil {
return err
}
return nil
}); err != nil {
return err
}
return nil
}
// GetMeta returns all of the saved metadata.
func (b *badgerStore) GetMeta() (map[string]json.RawMessage, error) {
res := make(map[string]json.RawMessage)
// Iterate all keys with matching prefix
// Adapted from Badger docs
if err := b.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
prefix := []byte(keyPrefix)
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
item := it.Item()
k := item.Key()
if err := item.Value(func(v []byte) error {
// Remember to strip prefix
strippedKey := strings.TrimPrefix(string(k), keyPrefix)
res[strippedKey] = json.RawMessage(v)
return nil
}); err != nil {
return err
}
}
return nil
}); err != nil {
return res, err
}
return res, nil
}

View File

@ -0,0 +1,31 @@
package badgerstore
import (
"encoding/json"
"testing"
"github.com/dgraph-io/badger/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBadgerStore(t *testing.T) {
badgerClient, err := badger.Open(badger.DefaultOptions("").WithInMemory(true))
require.NoError(t, err)
store := New(badgerClient)
expected := map[string]json.RawMessage{
"test": json.RawMessage("json"),
"test2": json.RawMessage("json2"),
"root.json": json.RawMessage(`[{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"0994148e5242118d1d6a9a397a3646e0423545a37794a791c28aa39de3b0c523"}}]`),
}
for k, v := range expected {
require.NoError(t, store.SetMeta(k, v))
}
res, err := store.GetMeta()
require.NoError(t, err)
assert.Equal(t, expected, res)
}

16
pkg/update/file.go Normal file
View File

@ -0,0 +1,16 @@
package update
import "os"
// fileDestination wraps the standard os.File with a Delete method for
// compatibility with the go-tuf Destination interface.
// Adapted from
// https://github.com/theupdateframework/go-tuf/blob/master/cmd/tuf-client/get.go
type fileDestination struct {
*os.File
}
func (f *fileDestination) Delete() error {
_ = f.Close()
return os.Remove(f.Name())
}

52
pkg/update/hash.go Normal file
View File

@ -0,0 +1,52 @@
package update
import (
"bytes"
"crypto/sha256"
"crypto/sha512"
"hash"
"io"
"os"
"github.com/pkg/errors"
"github.com/theupdateframework/go-tuf/data"
)
// CheckFileHash checks the file at the local path against the provided hash
// functions.
func CheckFileHash(meta *data.TargetFileMeta, localPath string) error {
hashFunc, hashVal, err := selectHashFunction(meta)
if err != nil {
return err
}
f, err := os.Open(localPath)
if err != nil {
return errors.Wrap(err, "open file for hash")
}
if _, err := io.Copy(hashFunc, f); err != nil {
return errors.Wrap(err, "read file for hash")
}
if !bytes.Equal(hashVal, hashFunc.Sum(nil)) {
return errors.Errorf("hash %s does not match expected: %s", data.HexBytes(hashFunc.Sum(nil)), data.HexBytes(hashVal))
}
return nil
}
// selectHashFunction returns the first matching hash function and expected
// hash, otherwise returning an error if not matching hash can be found.
func selectHashFunction(meta *data.TargetFileMeta) (hash.Hash, []byte, error) {
for hashName, hashVal := range meta.Hashes {
switch hashName {
case "sha512":
return sha512.New(), hashVal, nil
case "sha256":
return sha256.New(), hashVal, nil
}
}
return nil, nil, errors.Errorf("no matching hash function found: %v", meta.HashAlgorithms())
}

View File

@ -2,20 +2,19 @@
package update
import (
"bytes"
"crypto/sha512"
"crypto/tls"
"encoding/base64"
"io"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"path/filepath"
"github.com/fleetdm/orbit/pkg/constant"
"github.com/pkg/errors"
"github.com/theupdateframework/notary/client"
"github.com/theupdateframework/notary/trustpinning"
"github.com/theupdateframework/notary/tuf/data"
"github.com/theupdateframework/go-tuf/client"
"github.com/theupdateframework/go-tuf/data"
)
const (
@ -23,13 +22,14 @@ const (
osqueryDir = "osquery"
orbitDir = "orbit"
notaryDir = "notary"
windowsExtension = ".exe"
)
// Updater is responsible for managing update state.
type Updater struct {
opt Options
transport *http.Transport
client *client.Client
}
// Options are the options that can be provided when creating an Updater.
@ -38,13 +38,27 @@ type Options struct {
RootDirectory string
// ServerURL is the URL of the update server.
ServerURL string
// GUN is the Globally Unique Name to look up with the Notary server.
GUN string
// InsecureTransport skips TLS certificate verification in the transport if
// set to true.
InsecureTransport bool
// RootKeys is the JSON encoded root keys to use to bootstrap trust.
RootKeys string
// LocalStore is the local metadata store.
LocalStore client.LocalStore
}
var (
// DefaultOptions are the default options to use when creating an update
// client.
DefaultOptions = Options{
RootDirectory: "/var/fleet",
ServerURL: "https://tuf.fleetctl.com",
LocalStore: client.MemoryLocalStore(),
InsecureTransport: false,
RootKeys: `[{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"0994148e5242118d1d6a9a397a3646e0423545a37794a791c28aa39de3b0c523"}}]`,
}
)
// New creates a new updater given the provided options. All the necessary
// directories are initialized.
func New(opt Options) (*Updater, error) {
@ -52,40 +66,136 @@ func New(opt Options) (*Updater, error) {
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: opt.InsecureTransport,
}
httpClient := &http.Client{Transport: transport}
updater := &Updater{
opt: opt,
transport: transport,
remoteStore, err := client.HTTPRemoteStore(opt.ServerURL, nil, httpClient)
if err != nil {
return nil, errors.Wrap(err, "init remote store")
}
err := updater.initializeDirectories()
if err != nil {
tufClient := client.NewClient(opt.LocalStore, remoteStore)
var rootKeys []*data.Key
if err := json.Unmarshal([]byte(opt.RootKeys), &rootKeys); err != nil {
return nil, errors.Wrap(err, "unmarshal root keys")
}
if err := tufClient.Init(rootKeys, 1); err != nil {
return nil, errors.Wrap(err, "init tuf client")
}
updater := &Updater{
opt: opt,
client: tufClient,
}
if err := updater.initializeDirectories(); err != nil {
return nil, err
}
return updater, nil
}
// Lookup returns the target metadata for the provided GUN and target name.
func (u *Updater) Lookup(GUN, target string) (*client.Target, error) {
client, err := client.NewFileCachedRepository(
u.pathFromRoot(notaryDir),
data.GUN(GUN),
u.opt.ServerURL,
u.transport,
nil,
trustpinning.TrustPinConfig{},
)
func (u *Updater) UpdateMetadata() error {
if _, err := u.client.Update(); err != nil && errors.Is(err, &client.ErrLatestSnapshot{}) {
return errors.Wrap(err, "update metadata")
}
return nil
}
func makeRepoPath(name, platform, version string) string {
path := path.Join(name, platform, version, name)
if platform == "windows" {
path += windowsExtension
}
return path
}
func makeLocalPath(name, platform, version string) string {
path := filepath.Join(name, version, name)
if platform == "windows" {
path += windowsExtension
}
return path
}
// Lookup looks up the provided target in the local target metadata. This should
// be called after UpdateMetadata.
func (u *Updater) Lookup(name, platform, version string) (*data.TargetFileMeta, error) {
target, err := u.client.Target(makeRepoPath(name, platform, version))
if err != nil {
return nil, errors.Wrap(err, "make notary client")
return nil, errors.Wrapf(err, "lookup target %s", target)
}
targetWithRole, err := client.GetTargetByName(target)
return &target, nil
}
// Targets gets all of the known targets
func (u *Updater) Targets() (data.TargetFiles, error) {
targets, err := u.client.Targets()
if err != nil {
return nil, errors.Wrap(err, "get target by name")
return nil, errors.Wrapf(err, "get targets")
}
return &targetWithRole.Target, nil
return targets, nil
}
// Get returns the local path to the specified target. The target is downloaded
// if it does not yet exist locally or the hash does not match.
func (u *Updater) Get(name, platform, version string) (string, error) {
localPath := u.pathFromRoot(makeLocalPath(name, platform, version))
repoPath := makeRepoPath(name, platform, version)
stat, err := os.Stat(localPath)
if err != nil {
log.Println("error stat file:", err)
return localPath, u.Download(repoPath, localPath)
}
if !stat.Mode().IsRegular() {
return "", errors.Errorf("expected %s to be regular file", localPath)
}
meta, err := u.Lookup(name, platform, version)
if err != nil {
return "", err
}
if err := CheckFileHash(meta, localPath); err != nil {
log.Printf("Will redownload due to error checking hash: %v", err)
return localPath, u.Download(repoPath, localPath)
}
log.Printf("Found expected version locally: %s", localPath)
return localPath, nil
}
// Download downloads the target to the provided path. The file is deleted and
// an error is returned if the hash does not match.
func (u *Updater) Download(repoPath, localPath string) error {
tmp, err := ioutil.TempFile("", "orbit-download")
if err != nil {
return errors.Wrap(err, "open temp file for download")
}
defer func() {
tmp.Close()
os.Remove(tmp.Name())
}()
if err := os.MkdirAll(filepath.Dir(localPath), constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "initialize download dir")
}
if err := u.client.Download(repoPath, &fileDestination{tmp}); err != nil {
return errors.Wrapf(err, "download target %s", repoPath)
}
if err := os.Chmod(tmp.Name(), constant.DefaultExecutableMode); err != nil {
return errors.Wrap(err, "chmod download")
}
if err := os.Rename(tmp.Name(), localPath); err != nil {
return errors.Wrap(err, "move download")
}
return nil
}
func (u *Updater) pathFromRoot(parts ...string) string {
@ -97,7 +207,6 @@ func (u *Updater) initializeDirectories() error {
u.pathFromRoot(binDir),
u.pathFromRoot(binDir, osqueryDir),
u.pathFromRoot(binDir, orbitDir),
u.pathFromRoot(notaryDir),
} {
err := os.MkdirAll(dir, constant.DefaultDirMode)
if err != nil {
@ -107,54 +216,3 @@ func (u *Updater) initializeDirectories() error {
return nil
}
// DownloadWithSHA512Hash downloads the contents of the given URL, writing
// results to the provided writer. The size is used as an upper limit on the
// amount of data read. An error is returned if the hash of the data received
// does not match the expected hash.
func DownloadWithSHA512Hash(url string, out io.Writer, size int64, expectedHash []byte) error {
resp, err := http.Get(url)
if err != nil {
return errors.Wrap(err, "make get request")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.Errorf("unexpected HTTP status: %s", resp.Status)
}
hash := sha512.New()
// Limit size of response read to expected size
limitReader := &io.LimitedReader{
R: resp.Body,
N: size + 1,
}
// Tee the bytes through the hash function
teeReader := io.TeeReader(limitReader, hash)
n, err := io.Copy(out, teeReader)
if err != nil {
return errors.Wrap(err, "copy response body")
}
// Technically these cases would be caught by the hash, but these errors are
// hopefully a bit more helpful.
if n < size {
return errors.New("response smaller than expected")
}
if n > size {
return errors.New("response larger than expected")
}
// Validate the hash matches
gotHash := hash.Sum(nil)
if !bytes.Equal(gotHash, expectedHash) {
return errors.Errorf(
"hash %s does not match expected %s",
base64.StdEncoding.EncodeToString(gotHash),
base64.StdEncoding.EncodeToString(expectedHash),
)
}
return nil
}

View File

@ -1,12 +1,7 @@
package update
import (
"bytes"
"crypto/sha512"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
@ -15,103 +10,6 @@ import (
"github.com/stretchr/testify/require"
)
func TestDownloadWithSHA512HashInvalidURL(t *testing.T) {
t.Parallel()
err := DownloadWithSHA512Hash("localhost:12345569900", ioutil.Discard, 55, nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "make get request")
}
func TestDownloadWithSHA512HashErrorResponse(t *testing.T) {
t.Parallel()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "error", http.StatusInternalServerError)
}))
defer ts.Close()
err := DownloadWithSHA512Hash(ts.URL, ioutil.Discard, 55, nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "unexpected HTTP status")
}
func TestDownloadWithSHA512Hash(t *testing.T) {
t.Parallel()
expectedData := []byte("abc")
expectedHash, expectedLen := sha512Hash(expectedData), int64(len(expectedData))
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, string(expectedData))
}))
defer ts.Close()
var b bytes.Buffer
err := DownloadWithSHA512Hash(ts.URL, &b, expectedLen, expectedHash)
require.NoError(t, err)
assert.Equal(t, expectedData, b.Bytes())
}
func TestDownloadWithSHA512HashTooSmall(t *testing.T) {
t.Parallel()
expectedData := []byte("abc")
expectedHash, expectedLen := sha512Hash(expectedData), int64(len(expectedData))
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Don't write all of data
fmt.Fprint(w, string(expectedData[:2]))
}))
defer ts.Close()
err := DownloadWithSHA512Hash(ts.URL, ioutil.Discard, expectedLen, expectedHash)
require.Error(t, err)
assert.Contains(t, err.Error(), "small")
}
func TestDownloadWithSHA512HashTooLarge(t *testing.T) {
t.Parallel()
expectedData := []byte("abc")
expectedHash, expectedLen := sha512Hash(expectedData), int64(len(expectedData))
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Write additional data
fmt.Fprintf(w, string(expectedData)+"foobar")
}))
defer ts.Close()
err := DownloadWithSHA512Hash(ts.URL, ioutil.Discard, expectedLen, expectedHash)
require.Error(t, err)
assert.Contains(t, err.Error(), "large")
}
func TestDownloadWithSHA512HashMismatch(t *testing.T) {
t.Parallel()
expectedData := []byte("abc")
expectedHash, expectedLen := sha512Hash(expectedData), int64(len(expectedData))
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Write non-matching data
fmt.Fprint(w, string("def"))
}))
defer ts.Close()
err := DownloadWithSHA512Hash(ts.URL, ioutil.Discard, expectedLen, expectedHash)
require.Error(t, err)
assert.Contains(t, err.Error(), "not match")
}
func sha512Hash(data []byte) []byte {
hash := sha512.New()
if _, err := hash.Write(data); err != nil {
panic(err)
}
return hash.Sum(nil)
}
func TestInitializeDirectories(t *testing.T) {
t.Parallel()
@ -119,12 +17,14 @@ func TestInitializeDirectories(t *testing.T) {
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
_, err = New(Options{RootDirectory: tmpDir})
opt := DefaultOptions
opt.RootDirectory = tmpDir
updater := Updater{opt: opt}
err = updater.initializeDirectories()
require.NoError(t, err)
assertDir(t, filepath.Join(tmpDir, binDir))
assertDir(t, filepath.Join(tmpDir, binDir, osqueryDir))
assertDir(t, filepath.Join(tmpDir, binDir, orbitDir))
assertDir(t, filepath.Join(tmpDir, notaryDir))
}
func assertDir(t *testing.T, path string) {
@ -132,3 +32,25 @@ func assertDir(t *testing.T, path string) {
assert.NoError(t, err, "stat should succeed")
assert.True(t, info.IsDir())
}
func TestMakePath(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
platform string
version string
expected string
}{
{name: "osqueryd", platform: "linux", version: "4.6.0", expected: "osqueryd/linux/4.6.0/osqueryd"},
{name: "osqueryd", platform: "windows", version: "3.3.2", expected: "osqueryd/windows/3.3.2/osqueryd.exe"},
}
for _, tt := range testCases {
t.Run(tt.expected, func(t *testing.T) {
t.Parallel()
assert.Equal(t, tt.expected, makePath(tt.name, tt.platform, tt.version))
})
}
}