Add tls tapper (#683)

* initial tls tapper commit

* add tls flag to mizu cli

* support ssl_read_ex/ssl_write_ex

* use hostproc to find libssl

* auto discover tls processes

* support libssl1.0

* recompile ebpf with old clang/llvm

* Update tap/passive_tapper.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* Update tap/tlstapper/tls_poller.go

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>

* upgrade ebpf go lib

* handling big tls messages

* fixing max buffer size in ebpf

* remove unused import

* fix linter issues

* minor pr fixes

* compile with old clang

* fix cgroup file format

* pr fixes + cgroup extract enhance

* fix linter

* adding indirect ebpf dep to agent go.mod

* adding ebpf docker builder

* minor pr fixes

* add req resp matcher to dissect

* rename ssl hooks to ssl hooks structs

* move to alpine, use local copy of mizu instead of git, add readme

* use global req resp mather for tls

Co-authored-by: M. Mert Yıldıran <mehmet@up9.com>
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
This commit is contained in:
David Levanon
2022-02-16 15:34:51 +02:00
committed by GitHub
parent 72df652f6b
commit 87ef469e25
36 changed files with 2166 additions and 14 deletions

162
tap/tlstapper/tls_poller.go Normal file
View File

@@ -0,0 +1,162 @@
package tlstapper
import (
"bufio"
"fmt"
"net"
"encoding/hex"
"os"
"strconv"
"strings"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/tap/api"
)
const UNKNOWN_PORT uint16 = 80
const UNKNOWN_HOST string = "127.0.0.1"
type tlsPoller struct {
tls *TlsTapper
readers map[string]*tlsReader
closedReaders chan string
reqResMatcher api.RequestResponseMatcher
}
func NewTlsPoller(tls *TlsTapper, extension *api.Extension) *tlsPoller {
return &tlsPoller{
tls: tls,
readers: make(map[string]*tlsReader),
closedReaders: make(chan string, 100),
reqResMatcher: extension.Dissector.NewResponseRequestMatcher(),
}
}
func (p *tlsPoller) Poll(extension *api.Extension,
emitter api.Emitter, options *api.TrafficFilteringOptions) {
chunks := make(chan *tlsChunk)
go p.tls.pollPerf(chunks)
for {
select {
case chunk, ok := <-chunks:
if !ok {
return
}
if err := p.handleTlsChunk(chunk, extension, emitter, options); err != nil {
LogError(err)
}
case key := <-p.closedReaders:
delete(p.readers, key)
}
}
}
func (p *tlsPoller) handleTlsChunk(chunk *tlsChunk, extension *api.Extension,
emitter api.Emitter, options *api.TrafficFilteringOptions) error {
ip, port, err := chunk.getAddress()
if err != nil {
return err
}
key := buildTlsKey(chunk, ip, port)
reader, exists := p.readers[key]
if !exists {
reader = p.startNewTlsReader(chunk, ip, port, key, extension, emitter, options)
p.readers[key] = reader
}
reader.chunks <- chunk
if os.Getenv("MIZU_VERBOSE_TLS_TAPPER") == "true" {
logTls(chunk, ip, port)
}
return nil
}
func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, ip net.IP, port uint16, key string, extension *api.Extension,
emitter api.Emitter, options *api.TrafficFilteringOptions) *tlsReader {
reader := &tlsReader{
key: key,
chunks: make(chan *tlsChunk, 1),
doneHandler: func(r *tlsReader) {
p.closeReader(key, r)
},
}
isRequest := (chunk.isClient() && chunk.isWrite()) || (chunk.isServer() && chunk.isRead())
tcpid := buildTcpId(isRequest, ip, port)
go dissect(extension, reader, isRequest, &tcpid, emitter, options, p.reqResMatcher)
return reader
}
func dissect(extension *api.Extension, reader *tlsReader, isRequest bool, tcpid *api.TcpID,
emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher api.RequestResponseMatcher) {
b := bufio.NewReader(reader)
err := extension.Dissector.Dissect(b, isRequest, tcpid, &api.CounterPair{},
&api.SuperTimer{}, &api.SuperIdentifier{}, emitter, options, reqResMatcher)
if err != nil {
logger.Log.Warningf("Error dissecting TLS %v - %v", tcpid, err)
}
}
func (p *tlsPoller) closeReader(key string, r *tlsReader) {
close(r.chunks)
p.closedReaders <- key
}
func buildTlsKey(chunk *tlsChunk, ip net.IP, port uint16) string {
return fmt.Sprintf("%v:%v-%v:%v", chunk.isClient(), chunk.isRead(), ip, port)
}
func buildTcpId(isRequest bool, ip net.IP, port uint16) api.TcpID {
if isRequest {
return api.TcpID{
SrcIP: UNKNOWN_HOST,
DstIP: ip.String(),
SrcPort: strconv.Itoa(int(UNKNOWN_PORT)),
DstPort: strconv.FormatInt(int64(port), 10),
Ident: "",
}
} else {
return api.TcpID{
SrcIP: ip.String(),
DstIP: UNKNOWN_HOST,
SrcPort: strconv.FormatInt(int64(port), 10),
DstPort: strconv.Itoa(int(UNKNOWN_PORT)),
Ident: "",
}
}
}
func logTls(chunk *tlsChunk, ip net.IP, port uint16) {
var flagsStr string
if chunk.isClient() {
flagsStr = "C"
} else {
flagsStr = "S"
}
if chunk.isRead() {
flagsStr += "R"
} else {
flagsStr += "W"
}
str := strings.ReplaceAll(strings.ReplaceAll(string(chunk.Data[0:chunk.Recorded]), "\n", " "), "\r", "")
logger.Log.Infof("PID: %v (tid: %v) (fd: %v) (client: %v) (addr: %v:%v) (recorded %v out of %v) - %v - %v",
chunk.Pid, chunk.Tgid, chunk.Fd, flagsStr, ip, port, chunk.Recorded, chunk.Len, str, hex.EncodeToString(chunk.Data[0:chunk.Recorded]))
}