Skip to content

Commit

Permalink
implemented sni domain matching
Browse files Browse the repository at this point in the history
  • Loading branch information
superstes committed Sep 27, 2023
1 parent a53a93b commit 900b5b3
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 53 deletions.
3 changes: 3 additions & 0 deletions lib/proc/filter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ func Filter(pkt parse.ParsedPacket) bool {
matchDestinationPort(pkt, rule, rid) == meta.MatchNegative {
continue
}
if matchDomain(pkt, rule, rid) == meta.MatchNegative {
continue
}

ruleDebug(pkt, rid, fmt.Sprintf("Applying action '%v'", meta.RevRuleAction(rule.Action)))
return applyAction(rule.Action)
Expand Down
28 changes: 28 additions & 0 deletions lib/proc/filter/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package filter
import (
"fmt"
"net"
"strings"

"github.com/superstes/calamary/cnf"
"github.com/superstes/calamary/proc/meta"
Expand Down Expand Up @@ -129,6 +130,16 @@ func matchDestinationPort(pkt parse.ParsedPacket, rule cnf.Rule, rid int) meta.M
return meta.MatchNeutral
}

func matchDomain(pkt parse.ParsedPacket, rule cnf.Rule, rid int) meta.Match {
if rule.Match.Domains != nil && len(rule.Match.Domains) > 0 {
if pkt.L5.Proto == meta.ProtoL5Tls {
return ruleMatch(anyDomainMatch(rule.Match.Domains, pkt.L5.TlsSni))
}
// todo: add plain http domain-match
}
return meta.MatchNeutral
}

func anyProtoMatch(list []meta.Proto, single meta.Proto) bool {
for i := range list {
if list[i] == single {
Expand Down Expand Up @@ -156,6 +167,23 @@ func anyNetMatch(nets []*net.IPNet, ip net.IP) bool {
return false
}

func anyDomainMatch(domains []string, domain string) bool {
for i := range domains {
matchDomain := domains[i]
if strings.HasPrefix(matchDomain, "*.") {
matchDomain = strings.Replace(matchDomain, "*.", "", 1)
if strings.HasSuffix(domain, matchDomain) {
return true
}
} else {
if matchDomain == domain {
return true
}
}
}
return false
}

func ruleMatch(match bool) meta.Match {
if match {
return meta.MatchPositive
Expand Down
34 changes: 34 additions & 0 deletions lib/proc/filter/match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,37 @@ func TestMatchProtoL5(t *testing.T) {
t.Error("Match ProtoL5 #2")
}
}

func TestMatchDomain(t *testing.T) {
cnf.C = &cnf.Config{}
cnf.C.Service.Debug = false

pkt := parse.ParsedPacket{
L5: &parse.ParsedL5{
Proto: meta.ProtoL5Tls,
Encrypted: meta.OptBoolTrue,
TlsSni: "random.xxx",
},
}
rule1 := cnf.Rule{}
rule1.Match.Domains = []string{"superstes.eu", "*.calamary.net"}
if matchDomain(pkt, rule1, 1) != meta.MatchNegative {
t.Error("Match Domain #1")
}
pkt.L5.TlsSni = "superstes.eu"
if matchDomain(pkt, rule1, 1) != meta.MatchPositive {
t.Error("Match Domain #2")
}
pkt.L5.TlsSni = "calamary.net"
if matchDomain(pkt, rule1, 1) != meta.MatchPositive {
t.Error("Match Domain #3")
}
pkt.L5.TlsSni = "test.calamary.net"
if matchDomain(pkt, rule1, 1) != meta.MatchPositive {
t.Error("Match Domain #4")
}
pkt.L5.TlsSni = "abc.test.calamary.net"
if matchDomain(pkt, rule1, 1) != meta.MatchPositive {
t.Error("Match Domain #5")
}
}
21 changes: 21 additions & 0 deletions lib/proc/parse/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package parse

import (
"net/http"
"strings"

"github.com/superstes/calamary/cnf"
)

func hdrL5Http(hdr [cnf.L5HDRLEN]byte) bool {
s := string(hdr[:])
return strings.HasPrefix(http.MethodGet, s[:3]) ||
strings.HasPrefix(http.MethodPost, s[:4]) ||
strings.HasPrefix(http.MethodPut, s[:3]) ||
strings.HasPrefix(http.MethodDelete, s) ||
strings.HasPrefix(http.MethodOptions, s) ||
strings.HasPrefix(http.MethodPatch, s) ||
strings.HasPrefix(http.MethodHead, s[:4]) ||
strings.HasPrefix(http.MethodConnect, s) ||
strings.HasPrefix(http.MethodTrace, s)
}
54 changes: 1 addition & 53 deletions lib/proc/parse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ package parse

import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"net/http"
"strings"
"time"

"github.com/superstes/calamary/cnf"
"github.com/superstes/calamary/log"
"github.com/superstes/calamary/proc/meta"
tls_dissector "github.com/superstes/calamary/proc/parse/tls"
"github.com/superstes/calamary/u"
)

Expand Down Expand Up @@ -98,6 +94,7 @@ func parseTcp(conn net.Conn, connIo io.ReadWriter, hdr [cnf.L5HDRLEN]byte) Parse
pkt.L5.Proto = meta.ProtoL5Http
pkt.L5Http = &ParsedHttp{}
}
// todo: plain-http parsing

return pkt
}
Expand Down Expand Up @@ -147,52 +144,3 @@ func getL3Proto(ip net.IP) meta.Proto {
return meta.ProtoL3IP6
}
}

func parseTls(pkt ParsedPacket, conn net.Conn, connIo io.ReadWriter, hdr [cnf.L5HDRLEN]byte) (isTls meta.OptBool, tlsVersion uint16, sni string) {
isTlsRaw := hdr[0] == tls_dissector.Handshake
tlsVersion = binary.BigEndian.Uint16(hdr[1:3])
if isTlsRaw {
isTls = meta.OptBoolTrue
} else {
isTls = meta.OptBoolFalse
}

if isTlsRaw {
buf := new(bytes.Buffer)
r := io.TeeReader(connIo, buf)
record, err := tls_dissector.ReadRecord(r)
if err != nil {
return
}

clientHello := tls_dissector.ClientHelloMsg{}
if err = clientHello.Decode(record.Opaque); err != nil {
return
}

for _, ext := range clientHello.Extensions {
if ext.Type() == tls_dissector.ExtServerName {
snExtension := ext.(*tls_dissector.ServerNameExtension)
sni = snExtension.Name
break
}
}
}
log.ConnDebug("parse", PktSrc(pkt), PktDest(pkt), fmt.Sprintf(
"TLS information: IsTls=%v, TlsVersion=%v, TlsSni=%s", isTlsRaw, tlsVersion, sni,
))
return
}

func hdrL5Http(hdr [cnf.L5HDRLEN]byte) bool {
s := string(hdr[:])
return strings.HasPrefix(http.MethodGet, s[:3]) ||
strings.HasPrefix(http.MethodPost, s[:4]) ||
strings.HasPrefix(http.MethodPut, s[:3]) ||
strings.HasPrefix(http.MethodDelete, s) ||
strings.HasPrefix(http.MethodOptions, s) ||
strings.HasPrefix(http.MethodPatch, s) ||
strings.HasPrefix(http.MethodHead, s[:4]) ||
strings.HasPrefix(http.MethodConnect, s) ||
strings.HasPrefix(http.MethodTrace, s)
}
50 changes: 50 additions & 0 deletions lib/proc/parse/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package parse

import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"

"github.com/superstes/calamary/cnf"
"github.com/superstes/calamary/log"
"github.com/superstes/calamary/proc/meta"
tls_dissector "github.com/superstes/calamary/proc/parse/tls"
)

func parseTls(pkt ParsedPacket, conn net.Conn, connIo io.ReadWriter, hdr [cnf.L5HDRLEN]byte) (isTls meta.OptBool, tlsVersion uint16, sni string) {
isTlsRaw := hdr[0] == tls_dissector.Handshake
tlsVersion = binary.BigEndian.Uint16(hdr[1:3])
if isTlsRaw {
isTls = meta.OptBoolTrue
} else {
isTls = meta.OptBoolFalse
}

if isTlsRaw {
buf := new(bytes.Buffer)
r := io.TeeReader(connIo, buf)
record, err := tls_dissector.ReadRecord(r)
if err != nil {
return
}

clientHello := tls_dissector.ClientHelloMsg{}
if err = clientHello.Decode(record.Opaque); err != nil {
return
}

for _, ext := range clientHello.Extensions {
if ext.Type() == tls_dissector.ExtServerName {
snExtension := ext.(*tls_dissector.ServerNameExtension)
sni = snExtension.Name
break
}
}
}
log.ConnDebug("parse", PktSrc(pkt), PktDest(pkt), fmt.Sprintf(
"TLS information: IsTls=%v, TlsVersion=%v, TlsSni=%s", isTlsRaw, tlsVersion, sni,
))
return
}

0 comments on commit 900b5b3

Please sign in to comment.