logrecord.go
// Copyright 2018 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package monitor
import (
"encoding/json"
"fmt"
"strings"
"github.com/cilium/cilium/pkg/proxy/accesslog"
"github.com/miekg/dns"
)
// LogRecordNotify is a proxy access log notification
type LogRecordNotify struct {
accesslog.LogRecord
}
func (l *LogRecordNotify) direction() string {
switch l.ObservationPoint {
case accesslog.Ingress:
return "<-"
case accesslog.Egress:
return "->"
default:
return "??"
}
}
func (l *LogRecordNotify) l7Proto() string {
if l.HTTP != nil {
return "http"
}
if l.Kafka != nil {
return "kafka"
}
if l.DNS != nil {
return "dns"
}
if l.L7 != nil {
return l.L7.Proto
}
return "unknown-l7"
}
// DumpInfo dumps an access log notification
func (l *LogRecordNotify) DumpInfo() {
switch l.Type {
case accesslog.TypeRequest:
fmt.Printf("%s %s %s from %d (%s) to %d (%s), identity %d->%d, verdict %s",
l.direction(), l.Type, l.l7Proto(), l.SourceEndpoint.ID, l.SourceEndpoint.Labels,
l.DestinationEndpoint.ID, l.DestinationEndpoint.Labels,
l.SourceEndpoint.Identity, l.DestinationEndpoint.Identity,
l.Verdict)
case accesslog.TypeResponse:
fmt.Printf("%s %s %s to %d (%s) from %d (%s), identity %d->%d, verdict %s",
l.direction(), l.Type, l.l7Proto(), l.SourceEndpoint.ID, l.SourceEndpoint.Labels,
l.DestinationEndpoint.ID, l.DestinationEndpoint.Labels,
l.SourceEndpoint.Identity, l.DestinationEndpoint.Identity,
l.Verdict)
}
if http := l.HTTP; http != nil {
url := ""
if http.URL != nil {
url = http.URL.String()
}
fmt.Printf(" %s %s => %d\n", http.Method, url, http.Code)
}
if kafka := l.Kafka; kafka != nil {
fmt.Printf(" %s topic %s => %d\n", kafka.APIKey, kafka.Topic.Topic, kafka.ErrorCode)
}
if l.DNS != nil {
types := []string{}
for _, t := range l.DNS.QTypes {
types = append(types, dns.TypeToString[t])
}
qTypeStr := strings.Join(types, ",")
switch {
case l.Type == accesslog.TypeRequest:
fmt.Printf(" DNS Query: %s %s", l.DNS.Query, qTypeStr)
case l.Type == accesslog.TypeResponse:
sourceType := "Query"
if l.DNS.ObservationSource == accesslog.DNSSourceAgentPoller {
sourceType = "Poll"
}
fmt.Printf(" DNS %s: %s %s", sourceType, l.DNS.Query, qTypeStr)
ips := make([]string, 0, len(l.DNS.IPs))
for _, ip := range l.DNS.IPs {
ips = append(ips, ip.String())
}
fmt.Printf(" TTL: %d Answer: '%s'", l.DNS.TTL, strings.Join(ips, ","))
if len(l.DNS.CNAMEs) > 0 {
fmt.Printf(" CNAMEs: %s", strings.Join(l.DNS.CNAMEs, ","))
}
}
fmt.Printf("\n")
}
if l7 := l.L7; l7 != nil {
status := ""
for k, v := range l7.Fields {
if k == "status" {
status = v
} else {
fmt.Printf(" %s:%s", k, v)
}
}
if status != "" {
fmt.Printf(" => status:%s", status)
}
fmt.Printf("\n")
}
}
func (l *LogRecordNotify) getJSON() (string, error) {
v := LogRecordNotifyToVerbose(l)
ret, err := json.Marshal(v)
return string(ret), err
}
// DumpJSON prints notification in json format
func (l *LogRecordNotify) DumpJSON() {
resp, err := l.getJSON()
if err == nil {
fmt.Println(resp)
}
}
// LogRecordNotifyVerbose represents a json notification printed by monitor
type LogRecordNotifyVerbose struct {
Type string `json:"type"`
ObservationPoint accesslog.ObservationPoint `json:"observationPoint"`
FlowType accesslog.FlowType `json:"flowType"`
L7Proto string `json:"l7Proto"`
SrcEpID uint64 `json:"srcEpID"`
SrcEpLabels []string `json:"srcEpLabels"`
SrcIdentity uint64 `json:"srcIdentity"`
DstEpID uint64 `json:"dstEpID"`
DstEpLabels []string `json:"dstEpLabels"`
DstIdentity uint64 `json:"dstIdentity"`
Verdict accesslog.FlowVerdict `json:"verdict"`
HTTP *accesslog.LogRecordHTTP `json:"http,omitempty"`
Kafka *accesslog.LogRecordKafka `json:"kafka,omitempty"`
DNS *accesslog.LogRecordDNS `json:"dns,omitempty"`
L7 *accesslog.LogRecordL7 `json:"l7,omitempty"`
}
// LogRecordNotifyToVerbose turns LogRecordNotify into json-friendly Verbose structure
func LogRecordNotifyToVerbose(n *LogRecordNotify) LogRecordNotifyVerbose {
return LogRecordNotifyVerbose{
Type: "logRecord",
ObservationPoint: n.ObservationPoint,
FlowType: n.Type,
L7Proto: n.l7Proto(),
SrcEpID: n.SourceEndpoint.ID,
SrcEpLabels: n.SourceEndpoint.Labels,
SrcIdentity: n.SourceEndpoint.Identity,
DstEpID: n.DestinationEndpoint.ID,
DstEpLabels: n.DestinationEndpoint.Labels,
DstIdentity: n.DestinationEndpoint.Identity,
Verdict: n.Verdict,
HTTP: n.HTTP,
Kafka: n.Kafka,
DNS: n.DNS,
L7: n.L7,
}
}