diff --git a/audit.go b/audit.go index 649cb4d..8bc8631 100644 --- a/audit.go +++ b/audit.go @@ -4,6 +4,8 @@ import ( "errors" "flag" "fmt" + "github.com/allegro/bigcache" + "github.com/spf13/viper" "log" "log/syslog" "os" @@ -14,8 +16,6 @@ import ( "strconv" "strings" "syscall" - - "github.com/spf13/viper" ) var l = log.New(os.Stdout, "", 0) @@ -339,10 +339,13 @@ func main() { } nlClient, err := NewNetlinkClient(config.GetInt("socket_buffer.receive")) + if err != nil { el.Fatal(err) } + dnstapEnabled := config.GetBool("dnstap.enabled") + marshaller := NewAuditMarshaller( writer, uint16(config.GetInt("events.min")), @@ -355,6 +358,27 @@ func main() { l.Printf("Started processing events in the range [%d, %d]\n", config.GetInt("events.min"), config.GetInt("events.max")) + var dnstapClient *DnsTapClient + + if dnstapEnabled { + cacheCfg := bigcache.Config{ + LifeWindow: config.GetDuration("dnstap.record_ttl"), + HardMaxCacheSize: config.GetInt("dnstap.max_cache_size"), + MaxEntrySize: 96, + MaxEntriesInWindow: 1024, + Shards: 256, + } + + DnsMarshaller := NewDnsAuditMarshaller(marshaller, cacheCfg) + + dnstapClient, err = NewDnsTapClient(config, DnsMarshaller) + if err != nil { + el.Fatal(err) + } + + go dnstapClient.Receive() + } + //Main loop. Get data from netlink and send it to the json lib for processing for { msg, err := nlClient.Receive() @@ -367,6 +391,11 @@ func main() { continue } - marshaller.Consume(msg) + if dnstapClient != nil { + dnstapClient.DnsAuditMarshaller.Consume(msg) + } else { + marshaller.Consume(msg) + } + } } diff --git a/client.go b/client.go index 7538cb1..67a606d 100644 --- a/client.go +++ b/client.go @@ -4,10 +4,10 @@ import ( "bytes" "encoding/binary" "errors" + "fmt" "sync/atomic" "syscall" "time" - "fmt" ) // Endianness is an alias for what we assume is the current machine endianness diff --git a/ext_dnstap.go b/ext_dnstap.go new file mode 100644 index 0000000..7a78caa --- /dev/null +++ b/ext_dnstap.go @@ -0,0 +1,154 @@ +package main + +import ( + "fmt" + "net" + "os" + "os/user" + "strconv" + "strings" + + "github.com/dnstap/golang-dnstap" + "github.com/farsightsec/golang-framestream" + "github.com/golang/protobuf/proto" + "github.com/miekg/dns" + "github.com/spf13/viper" +) + +type DnsTapClient struct { + Listener net.Listener + DnsAuditMarshaller *DnsAuditMarshaller +} + +func NewDnsTapClient(config *viper.Viper, am *DnsAuditMarshaller) (*DnsTapClient, error) { + socket := config.GetString("dnstap.socket") + os.Remove(socket) + + listener, err := net.Listen("unix", socket) + if err != nil { + return nil, fmt.Errorf("Listen error: %s", err) + } + + socketOwner := config.GetString("dnstap.socket_owner") + + err = chown(socketOwner, socket) + + if err != nil { + el.Fatal(err) + } + + d := &DnsTapClient{ + Listener: listener, + DnsAuditMarshaller: am, + } + el.Printf("Started dnstap listener, opened input socket: %s", socket) + return d, nil +} + +func chown(socketOwner string, socket string) error { + u, err := user.Lookup(socketOwner) + if err != nil { + return fmt.Errorf("Could not find uid for user %s. Error: %s", socketOwner, err) + } + + uid, err := strconv.ParseInt(u.Uid, 10, 32) + if err != nil { + return fmt.Errorf("Found uid could not be parsed. Error: %s", err) + } + + g, err := user.LookupGroup(socketOwner) + if err != nil { + return fmt.Errorf("Could not find gid for group %s. Error: %s", socketOwner, err) + } + + gid, err := strconv.ParseInt(g.Gid, 10, 32) + if err != nil { + return fmt.Errorf("Found gid could not be parsed. Error: %s", err) + } + + if err = os.Chown(socket, int(uid), int(gid)); err != nil { + return fmt.Errorf("Could not chown output file. Error: %s", err) + } + return nil +} + +func (d *DnsTapClient) Receive() { + defer d.Listener.Close() + for { + conn, err := d.Listener.Accept() + if err != nil { + el.Printf("net.Listener.Accept() failed: %s\n", err) + continue + } + go d.Decode(conn) + } +} + +func (d *DnsTapClient) Decode(conn net.Conn) { + decoderOptions := &framestream.DecoderOptions{ + ContentType: []byte("protobuf:dnstap.Dnstap"), + Bidirectional: true, + } + dec, err := framestream.NewDecoder(conn, decoderOptions) + if err != nil { + el.Printf("framestream.NewDecoder failed: %s\n", err) + } + for { + frame, err := dec.Decode() + if err != nil { + el.Printf("framestream.Decoder.Decode() failed: %s\n", err) + break + } + dt := &dnstap.Dnstap{} + if err := proto.Unmarshal(frame, dt); err != nil { + el.Printf("dnstap.DnsOutput: proto.Unmarshal() failed: %s\n", err) + break + } + if dt.Message.ResponseMessage != nil { + d.cache(dt) + } + } +} + +func (d *DnsTapClient) cache(dt *dnstap.Dnstap) { + m := new(dns.Msg) + err := m.Unpack(dt.Message.ResponseMessage) + if err != nil { + el.Printf("msg.Unpack() failed: %s \n", err) + } else { + for i, r := range m.Answer { + host := strings.TrimRight(r.Header().Name, ".") + var record string + switch m.Answer[i].Header().Rrtype { + case dns.TypeA: + record = m.Answer[i].(*dns.A).A.String() + d.DnsAuditMarshaller.cache.Set(record, []byte(host)) + case dns.TypeAAAA: + record = m.Answer[i].(*dns.AAAA).AAAA.String() + d.DnsAuditMarshaller.cache.Set(record, []byte(host)) + case dns.TypeCNAME: + record := m.Answer[i].(*dns.CNAME).Target + d.DnsAuditMarshaller.cache.Set(record, []byte(host)) + } + if seq, ok := d.DnsAuditMarshaller.waitingForDNS[record]; ok { + if msg, ok := d.DnsAuditMarshaller.msgs[seq]; ok { + if !d.DnsAuditMarshaller.GotDNS[seq] && d.DnsAuditMarshaller.GotSaddr[seq] { + d.DnsAuditMarshaller.getDNS(msg) + } + d.DnsAuditMarshaller.completeMessage(seq) + } + delete(d.DnsAuditMarshaller.waitingForDNS, record) + } + + } + } +} + +func (dnsAm *DnsAuditMarshaller) getDNS(val *AuditMessageGroup) (ip string, host []byte) { + for _, msg := range val.Msgs { + if msg.Type == SOCKADDR { + ip, host = dnsAm.mapDns(msg) + } + } + return ip, host +} diff --git a/ext_marshaller_dnstap.go b/ext_marshaller_dnstap.go new file mode 100644 index 0000000..49d5477 --- /dev/null +++ b/ext_marshaller_dnstap.go @@ -0,0 +1,192 @@ +package main + +import ( + "encoding/hex" + "net" + + "os" + "strconv" + "syscall" + "time" + + "strings" + + "github.com/allegro/bigcache" +) + +type DnsAuditMarshaller struct { + *AuditMarshaller + waitingForDNS map[string]int + cache *bigcache.BigCache + GotSaddr map[int]bool + GotDNS map[int]bool +} + +func NewDnsAuditMarshaller(am *AuditMarshaller, cacheCfg bigcache.Config) *DnsAuditMarshaller { + c, cacheInitErr := bigcache.NewBigCache(cacheCfg) + + if cacheInitErr != nil { + el.Fatal(cacheInitErr) + } + + dnsAm := &DnsAuditMarshaller{ + am, + make(map[string]int), + c, + make(map[int]bool), + make(map[int]bool), + } + + return dnsAm +} + +// Ingests a netlink message and likely prepares it to be logged +func (a *DnsAuditMarshaller) Consume(nlMsg *syscall.NetlinkMessage) { + aMsg := NewAuditMessage(nlMsg) + + if aMsg.Seq == 0 { + // We got an invalid audit message, return the current message and reset + a.flushOld() + return + } + + if a.trackMessages { + a.detectMissing(aMsg.Seq) + } + + if nlMsg.Header.Type < a.eventMin || nlMsg.Header.Type > a.eventMax { + // Drop all audit messages that aren't things we care about or end a multi packet event + a.flushOld() + return + } + + val, ok := a.msgs[aMsg.Seq] + + if ok && nlMsg.Header.Type == EVENT_EOE && (a.GotDNS[aMsg.Seq] || !a.GotSaddr[aMsg.Seq]) { + a.completeMessage(aMsg.Seq) + return + } + + if ok { + if aMsg.Type == SOCKADDR { + a.mapDns(aMsg) + } + + // Mark if we don't have dns yet + if a.GotSaddr[aMsg.Seq] && !a.GotDNS[aMsg.Seq] { + ip, _ := a.mapDns(aMsg) + a.waitingForDNS[ip] = val.Seq + } + + if aMsg.Type != EVENT_EOE { + val.AddMessage(aMsg) + } + + } else { + // Create a new AuditMessageGroup + a.msgs[aMsg.Seq] = NewAuditMessageGroup(aMsg) + } + + a.flushOld() +} + +// Find all `saddr=` occurrences in a message and do a lookup +func (dnsAm *DnsAuditMarshaller) mapDns(am *AuditMessage) (ip string, host []byte) { + data := am.Data + start := 0 + end := 0 + + if start = strings.Index(data, "saddr="); start < 0 { + return + } + + // Progress the start point beyond the = sign + start += 6 + if end = strings.IndexByte(data[start:], spaceChar); end < 0 { + end = len(data) - start + if end > SOCKADDR_LENGTH { + return + } + } + + saddr := data[start : start+end] + + dnsAm.GotSaddr[am.Seq] = true + + var err error + + ip = parseAddr(saddr) + + host, err = dnsAm.cache.Get(ip) + if err == nil { + dnsAm.msgs[am.Seq].DnsMap[ip] = string(host) + } + return +} + +func parseFamily(saddr string) int64 { + a, err := strconv.ParseInt(saddr[0:2], 16, 32) + if err != nil { + el.Println(err) + } + + b, err := strconv.ParseInt(saddr[2:4], 16, 32) + if err != nil { + el.Println(err) + } + + return a + 256*b + +} + +func parseAddr(saddr string) (addr string) { + family := parseFamily(saddr) + + switch family { + case AF_INET: + b, err := hex.DecodeString(saddr[8:16]) + if err != nil { + el.Printf("unable to decode hex to bytes: %s", err) + } + addr = net.IP(b).String() + } + + return addr +} + +func (a *DnsAuditMarshaller) completeMessage(seq int) { + var msg *AuditMessageGroup + var ok bool + + if msg, ok = a.msgs[seq]; !ok { + //TODO: attempted to complete a missing message, log? + return + } + + if a.GotSaddr[seq] && !a.GotDNS[seq] { + a.getDNS(msg) + } + + if a.dropMessage(msg) { + delete(a.msgs, seq) + return + } + + if err := a.writer.Write(msg); err != nil { + el.Println("Failed to write message. Error:", err) + os.Exit(1) + } + + delete(a.msgs, seq) +} + +// Outputs any messages that are old enough +// This is because there is no indication of multi message events coming from kaudit +func (a *DnsAuditMarshaller) flushOld() { + now := time.Now() + for seq, msg := range a.msgs { + if msg.CompleteAfter.Before(now) || now.Equal(msg.CompleteAfter) { + a.completeMessage(seq) + } + } +} diff --git a/go-audit.yaml.example b/go-audit.yaml.example index 1ead083..450c305 100644 --- a/go-audit.yaml.example +++ b/go-audit.yaml.example @@ -6,6 +6,14 @@ socket_buffer: # Maximum max is net.core.rmem_max (/proc/sys/net/core/rmem_max) receive: 16384 +# Configure dnstap socket path if available +dnstap: + enabled: true + socket_owner: coredns + socket: /var/run/dnstap.sock + record_ttl: 300s + max_cache_size: 10 # in mb + events: # Minimum event type to capture, default 1300 min: 1300 diff --git a/marshaller_test.go b/marshaller_test.go index 45714fb..2721427 100644 --- a/marshaller_test.go +++ b/marshaller_test.go @@ -45,7 +45,7 @@ func TestAuditMarshaller_Consume(t *testing.T) { assert.Equal( t, - "{\"sequence\":1,\"timestamp\":\"10000001\",\"messages\":[{\"type\":1300,\"data\":\"hi there\"},{\"type\":1301,\"data\":\"hi there\"}],\"uid_map\":{}}\n", + "{\"sequence\":1,\"timestamp\":\"10000001\",\"messages\":[{\"type\":1300,\"data\":\"hi there\"},{\"type\":1301,\"data\":\"hi there\"}],\"uid_map\":{},\"dnstap\":{}}\n", w.String(), ) assert.Equal(t, 0, len(m.msgs)) @@ -113,7 +113,7 @@ func TestAuditMarshaller_Consume(t *testing.T) { m.Consume(new1320("0")) } - assert.Equal(t, "{\"sequence\":4,\"timestamp\":\"10000001\",\"messages\":[{\"type\":1300,\"data\":\"hi there\"}],\"uid_map\":{}}\n", w.String()) + assert.Equal(t, "{\"sequence\":4,\"timestamp\":\"10000001\",\"messages\":[{\"type\":1300,\"data\":\"hi there\"}],\"uid_map\":{},\"dnstap\":{}}\n", w.String()) expected := start.Add(time.Second * 2) assert.True(t, expected.Equal(time.Now()) || expected.Before(time.Now()), "Should have taken at least 2 seconds to flush") assert.Equal(t, 0, len(m.msgs)) diff --git a/parser.go b/parser.go index 84ba6fe..82fe1db 100644 --- a/parser.go +++ b/parser.go @@ -9,17 +9,24 @@ import ( "time" ) -var uidMap = map[string]string{} -var headerEndChar = []byte{")"[0]} -var headerSepChar = byte(':') -var spaceChar = byte(' ') - const ( + SYSCALL = 1300 // Syscall event + CONFIG_CHANGE = 1305 // Audit system configuration change + SOCKADDR = 1306 // Sockaddr copied as syscall arg + CWD = 1307 // Current working directory + EXECVE = 1309 // Execve arguments HEADER_MIN_LENGTH = 7 // Minimum length of an audit header HEADER_START_POS = 6 // Position in the audit header that the data starts COMPLETE_AFTER = time.Second * 2 // Log a message after this time or EOE + SOCKADDR_LENGTH = 34 // Length of saddr event + AF_INET = 2 // Address family for ipv4 ) +var uidMap = map[string]string{} +var headerEndChar = []byte{")"[0]} +var headerSepChar = byte(':') +var spaceChar = byte(' ') + type AuditMessage struct { Type uint16 `json:"type"` Data string `json:"data"` @@ -33,6 +40,7 @@ type AuditMessageGroup struct { CompleteAfter time.Time `json:"-"` Msgs []*AuditMessage `json:"messages"` UidMap map[string]string `json:"uid_map"` + DnsMap map[string]string `json:"dnstap"` Syscall string `json:"-"` } @@ -44,6 +52,7 @@ func NewAuditMessageGroup(am *AuditMessage) *AuditMessageGroup { AuditTime: am.AuditTime, CompleteAfter: time.Now().Add(COMPLETE_AFTER), UidMap: make(map[string]string, 2), // Usually only 2 individual uids per execve + DnsMap: make(map[string]string, 1), Msgs: make([]*AuditMessage, 0, 6), } @@ -89,9 +98,9 @@ func (amg *AuditMessageGroup) AddMessage(am *AuditMessage) { amg.Msgs = append(amg.Msgs, am) //TODO: need to find more message types that won't contain uids, also make these constants switch am.Type { - case 1309, 1307, 1306: + case EXECVE, CWD, SOCKADDR: // Don't map uids here - case 1300: + case SYSCALL: amg.findSyscall(am) amg.mapUids(am) default: @@ -110,7 +119,7 @@ func (amg *AuditMessageGroup) mapUids(am *AuditMessage) { break } - // Progress the start point beyon the = sign + // Progress the start point beyond the = sign start += 4 if end = strings.IndexByte(data[start:], spaceChar); end < 0 { // There was no ending space, maybe the uid is at the end of the line diff --git a/vendor/vendor.json b/vendor/vendor.json index 969cccc..c18edba 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -3,268 +3,233 @@ "ignore": "test", "package": [ { - "checksumSHA1": "hqDDDpue/5363luidNMBS8z8eJU=", - "path": "github.com/BurntSushi/toml", - "revision": "99064174e013895bbd9b025c31100bd1d9b590ca", - "revisionTime": "2016-07-17T15:07:09Z" + "checksumSHA1": "DNxS1GhVEsXnyzbz6fC+Zs/b8Y0=", + "path": "github.com/allegro/bigcache", + "revision": "70fdb6c1fbb8ef895f9222fa4552f88913cc05e4", + "revisionTime": "2018-10-09T05:01:24Z" }, { - "checksumSHA1": "Lf3uUXTkKK5DJ37BxQvxO1Fq+K8=", - "path": "github.com/davecgh/go-spew/spew", - "revision": "6d212800a42e8ab5c146b8ace3490ee17e5225f9", - "revisionTime": "2016-09-07T16:21:46Z" + "checksumSHA1": "zqToN+R6KybEskp1D4G/lAOKXU4=", + "path": "github.com/allegro/bigcache/queue", + "revision": "70fdb6c1fbb8ef895f9222fa4552f88913cc05e4", + "revisionTime": "2018-10-09T05:01:24Z" }, { - "checksumSHA1": "xgjI2W3RGiQwNlxsOW2V9fJ9kaM=", + "checksumSHA1": "NJ+mjEgf5/x6f+x8+IrQQcD7vko=", + "path": "github.com/dnstap/golang-dnstap", + "revision": "2cf77a2b5e11ac8d0ba3892772ac8e1f7b528344", + "revisionTime": "2017-08-29T15:17:10Z" + }, + { + "checksumSHA1": "O9mvmXE7QpYrb8st4l9SG7wud2s=", + "path": "github.com/farsightsec/golang-framestream", + "revision": "d0f7ed81b7afcc56d3c1a66a0fd26a1b609675f3", + "revisionTime": "2018-08-20T16:18:18Z" + }, + { + "checksumSHA1": "x2Km0Qy3WgJJnV19Zv25VwTJcBM=", "path": "github.com/fsnotify/fsnotify", - "revision": "f12c6236fe7b5cf6bcf30e5935d08cb079d78334", - "revisionTime": "2016-08-16T05:15:41Z" + "revision": "4da3e2cfbabc9f751898f250b49f2439785783a1", + "revisionTime": "2017-03-29T04:21:07Z" + }, + { + "checksumSHA1": "GaJLoEuMGnP5ofXvuweAI4wx06U=", + "path": "github.com/golang/protobuf/proto", + "revision": "ddf22928ea3c56eb4292a0adbbf5001b1e8e7d0d", + "revisionTime": "2018-10-05T18:17:28Z" }, { - "checksumSHA1": "fa9G5tEr4oJJc3vtgn/B0NWZXfA=", + "checksumSHA1": "HtpYAWHvd9mq+mHkpo7z8PGzMik=", "path": "github.com/hashicorp/hcl", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "67DfevLBglV52Y2eAuhFc/xQni0=", + "checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=", "path": "github.com/hashicorp/hcl/hcl/ast", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "l2oQxBsZRwn6eZjf+whXr8c9+8c=", + "checksumSHA1": "/15SVLnCDzxICSatuYbfctrcpSM=", "path": "github.com/hashicorp/hcl/hcl/parser", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" + }, + { + "checksumSHA1": "WR1BjzDKgv6uE+3ShcDTYz0Gl6A=", + "path": "github.com/hashicorp/hcl/hcl/printer", + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "lgR7PSAZ0RtvAc9OCtCnNsF/x8g=", + "checksumSHA1": "PYDzRc61T0pbwWuLNHgBRp/gJII=", "path": "github.com/hashicorp/hcl/hcl/scanner", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "JlZmnzqdmFFyb1+2afLyR3BOE/8=", + "checksumSHA1": "oS3SCN9Wd6D8/LG0Yx1fu84a7gI=", "path": "github.com/hashicorp/hcl/hcl/strconv", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { "checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=", "path": "github.com/hashicorp/hcl/hcl/token", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "jQ45CCc1ed/nlV7bbSnx6z72q1M=", + "checksumSHA1": "PwlfXt7mFS8UYzWxOK5DOq0yxS0=", "path": "github.com/hashicorp/hcl/json/parser", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "YdvFsNOMSWMLnY6fcliWQa0O5Fw=", + "checksumSHA1": "afrZ8VmAwfTdDAYVgNSXbxa4GsA=", "path": "github.com/hashicorp/hcl/json/scanner", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { "checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=", "path": "github.com/hashicorp/hcl/json/token", - "revision": "99df0eb941dd8ddbc83d3f3605a34f6a686ac85e", - "revisionTime": "2016-09-02T16:52:19Z" - }, - { - "checksumSHA1": "KQhA4EQp4Ldwj9nJZnEURlE6aQw=", - "path": "github.com/kr/fs", - "revision": "2788f0dbd16903de03cb8186e5c7d97b69ad387b", - "revisionTime": "2013-11-06T22:25:44Z" + "revision": "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8", + "revisionTime": "2017-10-17T18:19:29Z" }, { - "checksumSHA1": "eOXF2PEvYLMeD8DSzLZJWbjYzco=", - "path": "github.com/kr/pretty", - "revision": "cfb55aafdaf3ec08f0db22699ab822c50091b1c4", - "revisionTime": "2016-08-23T17:07:15Z" - }, - { - "checksumSHA1": "uulQHQ7IsRKqDudBC8Go9J0gtAc=", - "path": "github.com/kr/text", - "revision": "7cafcd837844e784b526369c9bce262804aebc60", - "revisionTime": "2016-05-04T02:26:26Z" - }, - { - "checksumSHA1": "S6PDDQMYaKwLDIP/NsRYb4FRAqQ=", + "checksumSHA1": "NiPRC0JDsfCFir75S1TrFHPP8+M=", "path": "github.com/magiconair/properties", - "revision": "0723e352fa358f9322c938cc2dadda874e9151a9", - "revisionTime": "2016-09-08T09:36:58Z" + "revision": "2c9e9502788518c97fe44e8955cd069417ee89df", + "revisionTime": "2018-02-17T13:45:45Z" }, { - "checksumSHA1": "LUrnGREfnifW4WDMaavmc9MlLI0=", - "path": "github.com/mitchellh/mapstructure", - "revision": "ca63d7c062ee3c9f34db231e352b60012b4fd0c1", - "revisionTime": "2016-08-08T18:12:53Z" + "checksumSHA1": "KycBRsg27SKMCVnRgChcQOqviPA=", + "path": "github.com/miekg/dns", + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "8Y05Pz7onrQPcVWW6JStSsYRh6E=", - "path": "github.com/pelletier/go-buffruneio", - "revision": "df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d", - "revisionTime": "2016-01-24T19:35:03Z" + "checksumSHA1": "FpgODaspeA2JtrcagXl9JRY/i88=", + "path": "github.com/mitchellh/mapstructure", + "revision": "a4e142e9c047c904fa2f1e144d9a84e6133024bc", + "revisionTime": "2018-02-03T10:28:30Z" }, { - "checksumSHA1": "8p2QlEz7wJdeT4RQINh1mQgB0YQ=", + "checksumSHA1": "+qobk0BQiCfqdrhKeoZnwz6hFnE=", "path": "github.com/pelletier/go-toml", - "revision": "31055c2ff0bb0c7f9095aec0d220aed21108121e", - "revisionTime": "2016-09-06T20:25:57Z" - }, - { - "checksumSHA1": "Hky3u+8Rqum+wB5BHMj0A8ZmT4g=", - "path": "github.com/pkg/errors", - "revision": "17b591df37844cde689f4d5813e5cea0927d8dd2", - "revisionTime": "2016-08-22T09:00:10Z" - }, - { - "checksumSHA1": "Se8D92CQFRScBhugM3JFKSmwwzU=", - "path": "github.com/pkg/profile", - "revision": "303fad789382e54372c3b92956e55fadf81b413d", - "revisionTime": "2016-08-22T09:56:20Z" - }, - { - "checksumSHA1": "v6/DDmWObvEsdMvLe+KfuBVSNtg=", - "path": "github.com/pkg/sftp", - "revision": "8197a2e580736b78d704be0fc47b2324c0591a32", - "revisionTime": "2016-09-08T10:00:35Z" - }, - { - "checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=", - "path": "github.com/pmezard/go-difflib/difflib", - "revision": "792786c7400a136282c1664665ae0a8db921c6c2", - "revisionTime": "2016-01-10T10:55:54Z" + "revision": "acdc4509485b587f5e675510c4f2c63e90ff68a8", + "revisionTime": "2018-01-18T22:54:55Z" }, { - "checksumSHA1": "59wTbS4fE2282Q88NrBYImbFGbo=", + "checksumSHA1": "F3JU4T4XXvZTRAcv6rhTao4QGos=", "path": "github.com/spf13/afero", - "revision": "20500e2abd0d1f4564a499e83d11d6c73cd58c27", - "revisionTime": "2016-08-21T08:36:12Z" + "revision": "bbf41cb36dffe15dff5bf7e18c447801e7ffe163", + "revisionTime": "2018-02-11T16:21:14Z" }, { - "checksumSHA1": "S29tnboEqKV8/ghlvDzaqtJJD7E=", + "checksumSHA1": "X6RueW0rO55PbOQ0sMWSQOxVl4I=", "path": "github.com/spf13/afero/mem", - "revision": "20500e2abd0d1f4564a499e83d11d6c73cd58c27", - "revisionTime": "2016-08-21T08:36:12Z" + "revision": "bbf41cb36dffe15dff5bf7e18c447801e7ffe163", + "revisionTime": "2018-02-11T16:21:14Z" }, { - "checksumSHA1": "sLyAUiIT7V0DNVp6yBhW4Ms5BEs=", - "path": "github.com/spf13/afero/sftp", - "revision": "20500e2abd0d1f4564a499e83d11d6c73cd58c27", - "revisionTime": "2016-08-21T08:36:12Z" - }, - { - "checksumSHA1": "F5PKdeFzODdoAChY/aEiEDaNWtQ=", + "checksumSHA1": "Hc2i9OOK34PAImuNftTaHdbdLgs=", "path": "github.com/spf13/cast", - "revision": "e31f36ffc91a2ba9ddb72a4b6a607ff9b3d3cb63", - "revisionTime": "2016-07-30T09:20:37Z" + "revision": "8965335b8c7107321228e3e3702cab9832751bac", + "revisionTime": "2018-02-14T17:35:30Z" }, { - "checksumSHA1": "dkruahfhuLXXuyeCuRpsWlcRK+8=", + "checksumSHA1": "+JFKK0z5Eutk29rUz1lEhLxHMfk=", "path": "github.com/spf13/jwalterweatherman", - "revision": "33c24e77fb80341fe7130ee7c594256ff08ccc46", - "revisionTime": "2016-03-01T12:00:06Z" + "revision": "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394", + "revisionTime": "2018-01-09T13:55:06Z" }, { - "checksumSHA1": "AdV2H8HNLmUZbKzDdGme3s1gffI=", + "checksumSHA1": "tfKmT8uovT9Ch2YyDJtti3egAB4=", "path": "github.com/spf13/pflag", - "revision": "7b17cc4658ef5ca157b986ea5c0b43af7938532b", - "revisionTime": "2016-09-11T19:46:38Z" + "revision": "ee5fd03fd6acfd43e44aea0b4135958546ed8e73", + "revisionTime": "2018-02-20T14:32:36Z" }, { - "checksumSHA1": "cNe3MKwsFLDRzRjKEtOduUiG344=", + "checksumSHA1": "GWX9W5F1QBqLZsS1bYsG3jXjb3g=", "path": "github.com/spf13/viper", - "revision": "7538d73b4eb9511d85a9f1dfef202eeb8ac260f4", - "revisionTime": "2017-02-17T16:38:17Z" + "revision": "aafc9e6bc7b7bb53ddaa75a5ef49a17d6e654be5", + "revisionTime": "2017-11-29T09:51:06Z" }, { - "checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=", - "path": "github.com/stretchr/testify/assert", - "revision": "d77da356e56a7428ad25149ca77381849a6a5232", - "revisionTime": "2016-06-15T09:26:46Z" - }, - { - "checksumSHA1": "h+pFYiRHBogczS8/F1NoN3Ata44=", - "path": "golang.org/x/crypto/curve25519", - "revision": "119f50887f8fe324fe2386421c27a11af014b64e", - "revisionTime": "2016-05-18T18:29:53Z" - }, - { - "checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=", + "checksumSHA1": "2LpxYGSf068307b7bhAuVjvzLLc=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/crypto/ed25519", "path": "golang.org/x/crypto/ed25519", - "revision": "119f50887f8fe324fe2386421c27a11af014b64e", - "revisionTime": "2016-05-18T18:29:53Z" + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=", + "checksumSHA1": "0JTAFXPkankmWcZGQJGScLDiaN8=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/crypto/ed25519/internal/edwards25519", "path": "golang.org/x/crypto/ed25519/internal/edwards25519", - "revision": "119f50887f8fe324fe2386421c27a11af014b64e", - "revisionTime": "2016-05-18T18:29:53Z" + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "pXOcpeiBjX3zbVKeg0pvYnEmtqU=", - "path": "golang.org/x/crypto/ssh", - "revision": "119f50887f8fe324fe2386421c27a11af014b64e", - "revisionTime": "2016-05-18T18:29:53Z" + "checksumSHA1": "NjyXtXsaf0ulRJn6HQSP1FqGL4A=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/net/bpf", + "path": "golang.org/x/net/bpf", + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "8fD/im5Kwvy3JgmxulDTambmE8w=", - "path": "golang.org/x/sys/unix", - "revision": "30de6d19a3bd89a5f38ae4028e23aaa5582648af", - "revisionTime": "2016-09-07T05:59:14Z" + "checksumSHA1": "CHpYrf4HcmHB60gnTNxgjGgY53w=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/net/internal/iana", + "path": "golang.org/x/net/internal/iana", + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "3JkLagg7UP4890bHn0Uld6GmO6M=", - "path": "golang.org/x/text/internal/gen", - "revision": "f6f58eac0a9f37bcb49f8c8010c24cd382bf862d", - "revisionTime": "2016-08-30T13:53:28Z" + "checksumSHA1": "wUFe08HvcVTwkXK7dojH3W7CQVg=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/net/internal/socket", + "path": "golang.org/x/net/internal/socket", + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "47nwiUyVBY2RKoEGXmCSvusY4Js=", - "path": "golang.org/x/text/internal/triegen", - "revision": "f6f58eac0a9f37bcb49f8c8010c24cd382bf862d", - "revisionTime": "2016-08-30T13:53:28Z" + "checksumSHA1": "NITWzmU2rmFojwdCaxjbvR26B7U=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/net/ipv4", + "path": "golang.org/x/net/ipv4", + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "LyT5byg0Dq4x3OcGt6EwIDgPUpc=", - "path": "golang.org/x/text/internal/ucd", - "revision": "f6f58eac0a9f37bcb49f8c8010c24cd382bf862d", - "revisionTime": "2016-08-30T13:53:28Z" + "checksumSHA1": "arKhUYK+A0oKiB8kCQbtc+S6RM4=", + "origin": "github.com/miekg/dns/vendor/golang.org/x/net/ipv6", + "path": "golang.org/x/net/ipv6", + "revision": "ba6747e8a94115e9dc7738afb87850687611df1b", + "revisionTime": "2018-09-29T16:16:31Z" }, { - "checksumSHA1": "ziMb9+ANGRJSSIuxYdRbA+cDRBQ=", - "path": "golang.org/x/text/transform", - "revision": "f6f58eac0a9f37bcb49f8c8010c24cd382bf862d", - "revisionTime": "2016-08-30T13:53:28Z" + "checksumSHA1": "rEL6Z+R2saAWNv45gP6ZxoLgcEE=", + "path": "golang.org/x/sys/unix", + "revision": "e4b3c5e9061176387e7cea65e4dc5853801f3fb7", + "revisionTime": "2018-09-28T10:55:38Z" }, { - "checksumSHA1": "n94g6qdzv0fgQFGelH4/HXOthl0=", - "path": "golang.org/x/text/unicode/cldr", - "revision": "f6f58eac0a9f37bcb49f8c8010c24cd382bf862d", - "revisionTime": "2016-08-30T13:53:28Z" + "checksumSHA1": "ziMb9+ANGRJSSIuxYdRbA+cDRBQ=", + "path": "golang.org/x/text/transform", + "revision": "88f656faf3f37f690df1a32515b479415e1a6769", + "revisionTime": "2017-10-26T07:52:28Z" }, { - "checksumSHA1": "Aj3JSVO324FCjEAGm4ZwmC79bbo=", + "checksumSHA1": "BwRNKgzIMUxk56OScxyr43BV6IE=", "path": "golang.org/x/text/unicode/norm", - "revision": "f6f58eac0a9f37bcb49f8c8010c24cd382bf862d", - "revisionTime": "2016-08-30T13:53:28Z" - }, - { - "checksumSHA1": "93uHIq25lffEKY47PV8dBPD+XuQ=", - "path": "gopkg.in/fsnotify.v1", - "revision": "a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb", - "revisionTime": "2016-06-29T01:11:04Z" + "revision": "88f656faf3f37f690df1a32515b479415e1a6769", + "revisionTime": "2017-10-26T07:52:28Z" }, { - "checksumSHA1": "SPMXWeoFQa5z0pLPmqpcFzyHqQQ=", + "checksumSHA1": "fALlQNY1fM99NesfLJ50KguWsio=", "path": "gopkg.in/yaml.v2", - "revision": "31c299268d302dd0aa9a0dcf765a3d58971ac83f", - "revisionTime": "2016-09-12T16:56:03Z" + "revision": "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b", + "revisionTime": "2017-04-07T17:21:22Z" } ], "rootPath": "github.com/slackhq/go-audit"